Exemplo n.º 1
0
 def _check_params(cls, interval_obj, interval_sec, poll_sec, interval_cb, time_func):
     if not hasattr(interval_obj, 'update'):
         raise TOSDB_Error("'interval obj' has no 'update' function/attribute")
     try:
         test = interval_obj([0], 30, 1)
     except Exception as e:
         raise TOSDB_Error("could not instantiate 'interval_obj': %s" % str(e))
     if interval_sec < cls.MIN_INTERVAL_SEC:
         raise TOSDB_Error("interval_sec(%i) < MIN_INTERVAL_SEC(%i)" \
                           % (interval_sec, cls.MIN_INTERVAL_SEC) )        
     if interval_sec > cls.MAX_INTERVAL_SEC:
         raise TOSDB_Error("interval_sec(%i) > MAX_INTERVAL_SEC(%i)" \
                           % (interval_sec, cls.MAX_INTERVAL_SEC) )        
     if interval_sec < (2*poll_sec):
         raise TOSDB_Error("interval_sec(%i) < 2 * poll_sec(%i)" \
                           % (interval_sec, poll_sec))
     if poll_sec < cls.MIN_POLL_SEC:
         raise TOSDB_Error("poll_sec(%i) < MIN_POLL_SEC(%i)" \
                           % (poll_sec, cls.MIN_POLL_SEC) )
     if interval_cb is not None:
         if not callable(interval_cb):
             raise TOSDB_Error("'interval_cb' not callable")
         if len(_signature(interval_cb).parameters) != 3:
             raise TOSDB_Error("'interval_cb' signature requires 3 paramaters")
     if not callable(time_func):
         raise TOSDB_Error("'time_func' not callable")
Exemplo n.º 2
0
 def _check_params(cls, interval_obj, interval_sec, poll_sec, interval_cb,
                   time_func):
     if not hasattr(interval_obj, 'update'):
         raise TOSDB_Error(
             "'interval obj' has no 'update' function/attribute")
     try:
         test = interval_obj([0], 30, 1)
     except Exception as e:
         raise TOSDB_Error("could not instantiate 'interval_obj': %s" %
                           str(e))
     if interval_sec < cls.MIN_INTERVAL_SEC:
         raise TOSDB_Error("interval_sec(%i) < MIN_INTERVAL_SEC(%i)" \
                           % (interval_sec, cls.MIN_INTERVAL_SEC) )
     if interval_sec > cls.MAX_INTERVAL_SEC:
         raise TOSDB_Error("interval_sec(%i) > MAX_INTERVAL_SEC(%i)" \
                           % (interval_sec, cls.MAX_INTERVAL_SEC) )
     if interval_sec < (2 * poll_sec):
         raise TOSDB_Error("interval_sec(%i) < 2 * poll_sec(%i)" \
                           % (interval_sec, poll_sec))
     if poll_sec < cls.MIN_POLL_SEC:
         raise TOSDB_Error("poll_sec(%i) < MIN_POLL_SEC(%i)" \
                           % (poll_sec, cls.MIN_POLL_SEC) )
     if interval_cb is not None:
         if not callable(interval_cb):
             raise TOSDB_Error("'interval_cb' not callable")
         if len(_signature(interval_cb).parameters) != 3:
             raise TOSDB_Error(
                 "'interval_cb' signature requires 3 paramaters")
     if not callable(time_func):
         raise TOSDB_Error("'time_func' not callable")
    def decorator(func):
        """Decorator factory, assigns arguments as keyword-only and
        calculates sets for error checking.

        Args:
          func: The function to decorate.

        Returns:
          A function wrapped so that it has keyword-only arguments. 
        """
        signature = _signature(func)
        kw_only_args = set(included_keywords)
        positional_args = set()
        args_with_defaults = set()
        for name, param in signature.parameters.items():
            if param.kind in {param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD}:
                positional_args.add(name)
            if not included_keywords and param.kind is param.POSITIONAL_OR_KEYWORD and param.default is not param.empty:
                kw_only_args.add(name)
            if param.default is not param.empty:
                args_with_defaults.add(name)

        @functools.wraps(func)
        def wrapper(*args, **kws):
            """The decorator itself, checks arguments with set operations, moves
            args from *args into **kws, and then calls func().

            Args:
              *args, **kws: The arguments passed to the original function.

            Returns:
              The original function's result when it's called with the
              modified arguments.

            Raises:
              TypeError: When there is a mismatch between the supplied
                and expected arguments.

            """
            keys = collections.KeysView(kws)
            # Are all the keyword-only args covered either by a passed
            # argument or a default?
            if not kw_only_args <= keys | args_with_defaults:
                wrong_args(func, signature, kw_only_args - (keys | args_with_defaults), 'keyword-only')
            # Are there enough positional args to cover all the
            # arguments not covered by a passed argument or a default?
            if len(args) < len(positional_args - (keys | args_with_defaults)):
                wrong_args(func, signature, positional_args - (keys | args_with_defaults), 'positional', len(args))

            args = list(args)
            for index, (name, param) in enumerate(signature.parameters.items()):
                if param.kind is param.VAR_POSITIONAL or param.kind is param.VAR_KEYWORD:
                    break
                if name in kw_only_args or name in keys & positional_args:
                    args.insert(index, kws.pop(name, param.default))
            func(*args, **kws)
        return wrapper
Exemplo n.º 4
0
    def curry_1(*args):
        if len(args) >= len(_signature(func).parameters):
            return func(*args)
        else:

            def curry_2(*other_args):
                return curry(func)(*args, *other_args)

            return curry_2
Exemplo n.º 5
0
    def _prepare_request(self, method, url, kwargs):
        '''
        Take the request information and transform it into a prepared_request obj.

        requests.models.PreparedRequest is used to ensure that the url, headers, and body match
        exactly what request is sending to the API.
        '''
        prepared_request = requests.models.PreparedRequest()
        sig = _signature(prepared_request.prepare)
        prepared_kwargs = {x: y for x, y in kwargs.items() if x in sig.parameters}
        prepared_request.prepare(method=method, url=url, **prepared_kwargs)
        return prepared_request
Exemplo n.º 6
0
def get_args(func):
    """Get a list of argument names for a function.

    :param func: The function to inspect.

    :return: A list of argument names.
    :rtype: list
    """
    params = _signature(func).parameters.values()
    return [
        param.name for param in params
        if param.kind == param.POSITIONAL_OR_KEYWORD
    ]
Exemplo n.º 7
0
 def _paramlist(obj):
     try:
         sig = _signature(obj)
         params = []
         for p in sig.parameters.values():
             if p.default is not p.empty:
                 params.append('{}={}'.format(
                     p.name,
                     getattr(p.default, '__name__', repr(p.default))
                 ))
             else:
                 params.append(p.name)
         return params
     except (ValueError, TypeError):
         return ['?']
Exemplo n.º 8
0
    def _wrapper(func):
        fun_ret = _signature(func).return_annotation
        if len(keys) == 1:

            @_wraps(func)
            def wrapper(*args, **kwargs):
                return {keys[0]: func(*args, **kwargs)}
        elif isinstance(fun_ret, Iterable) and len(
                str(fun_ret)[12:].split(',')) == len(keys):

            @_wraps(func)
            def wrapper(*args, **kwargs) -> Dict[str, Union[str, _Real]]:
                data = {k: v for k, v in zip(keys, func(*args, **kwargs))}
                # data['ts'] = _dt.now().strftime(TS_PATTERN)
                return data
        else:
            raise ValueError('Keys mismatch to function returns annotation')
        return wrapper
Exemplo n.º 9
0
    def real_decorator(func):
        fun_ret = _signature(func).return_annotation
        if len(keys) == 1:

            @_wraps(func)
            def wrapper(*args, **kwargs):
                datum = func(*args, **kwargs)
                pub.sendMessage(topic, data={keys[0]: datum})
                return datum
        elif len(str(fun_ret)[12:].split(',')) == len(keys):

            @_wraps(func)
            def wrapper(*args, **kwargs):
                datum = func(*args, **kwargs)
                pub.sendMessage(topic,
                                data={k: v
                                      for k, v in zip(keys, datum)})
                return datum
        else:
            raise ValueError('Keys mismatch to function return annotation')
        return wrapper
Exemplo n.º 10
0
 def decorator(overriden: Callable[..., Any]) -> Callable[..., Any]:
     overriden.__doc__ = original.__doc__
     overriden.__signature__ = _signature(original)  # type: ignore
     return overriden
Exemplo n.º 11
0
def signature(func, use_original=False):
    """Better signature function"""
    return _signature(func) if use_original else (getattr(func, '__signature__', None) or _signature(func))
Exemplo n.º 12
0
    def decorator(func):
        """Decorator factory, assigns arguments as keyword-only and
        calculates sets for error checking.

        Args:
          func: The function to decorate.

        Returns:
          A function wrapped so that it has keyword-only arguments. 
        """
        signature = _signature(func)
        kw_only_args = set(included_keywords)
        positional_args = set()
        args_with_defaults = set()
        for name, param in signature.parameters.items():
            if param.kind in {
                    param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD
            }:
                positional_args.add(name)
            if not included_keywords and param.kind is param.POSITIONAL_OR_KEYWORD and param.default is not param.empty:
                kw_only_args.add(name)
            if param.default is not param.empty:
                args_with_defaults.add(name)

        @functools.wraps(func)
        def wrapper(*args, **kws):
            """The decorator itself, checks arguments with set operations, moves
            args from *args into **kws, and then calls func().

            Args:
              *args, **kws: The arguments passed to the original function.

            Returns:
              The original function's result when it's called with the
              modified arguments.

            Raises:
              TypeError: When there is a mismatch between the supplied
                and expected arguments.

            """
            keys = collections.KeysView(kws)
            # Are all the keyword-only args covered either by a passed
            # argument or a default?
            if not kw_only_args <= keys | args_with_defaults:
                wrong_args(func, signature,
                           kw_only_args - (keys | args_with_defaults),
                           'keyword-only')
            # Are there enough positional args to cover all the
            # arguments not covered by a passed argument or a default?
            if len(args) < len(positional_args - (keys | args_with_defaults)):
                wrong_args(func, signature,
                           positional_args - (keys | args_with_defaults),
                           'positional', len(args))

            args = list(args)
            for index, (name,
                        param) in enumerate(signature.parameters.items()):
                if param.kind is param.VAR_POSITIONAL or param.kind is param.VAR_KEYWORD:
                    break
                if name in kw_only_args or name in keys & positional_args:
                    args.insert(index, kws.pop(name, param.default))
            func(*args, **kws)

        return wrapper
Exemplo n.º 13
0
def circle_plot_residues(fragments,
                         fontsize=None,
                         colors=True,
                         markersize=None,
                         r=1,
                         panelsize=4,
                         angle_offset=0,
                         padding=[1, 1, 1],
                         center=[0, 0],
                         ss_array=None,
                         fragment_names=None,
                         iax=None,
                         textlabels=True,
                         shortenAAs=True,
                         highlight_residxs=None,
                         aa_offset=0,
                         top=None,
                         arc=False,
                         aura=None):
    r"""
    Circular background that serves as background for flare-plots. Is independent of
    the curves that will later be plotted onto it.

    Parameters
    ----------
    fragments : list
        List of iterables of residue idxs defining how the
        residues are split into fragments. If no
        :obj:`textlabels` are provided, the idxs
        themselves become the labels
    r : scalar
        The radius of the circle, in axis inuts
    angle_offset : scalar
        Where the circle starts, in degrees. 0 means 3 o'clock,
        90 12 o'clock etc. It's the phi of polar coordinates
    padding : list, default is [1,1,1]
        * first integer : Put this many empty positions before the first dot
        * second integer: Put this many empty positions between fragments
        * third integer : Put this many empty positions after the last dot
    center : pair of floats
        where the circle is centered, in axis units
    ss_array : dict, list or array
        One-letter codes (H,B,E,C) denoting secondary
        structure. Has to be indexable by whatever
        indices are on :obj:`fragments`
    fragment_names : list
        The names of the fragments
    iax : :obj:`~matplotlib.axes.Axes`, default is None
        An axis to draw the dots on. It's parent
        figure has to have a tight_layout=True
        attribute. If no axis is passed,
        one will be created.
    textlabels : bool or array_like, default is True
        How to label the residue dots
         * True: the dots representing the residues
           will get a label automatically, either their
           serial index or the residue name, e.g. GLU30, if
           a :obj:`top` was passed.
         * False: no labeling
         * array_like : will be passed as :obj:`replacement_labels`
           to :obj:`mdciao.flare.add_fragmented_residue_labels`.
           These labels act as replacement and can cover all or just
           some residue, e.g. a mutated residue that you want
           to show as R38A instead of just A38, or use
           e.g. BW or CGN consensus labels.
    fontsize
    colors
    markersize
    top : :obj:`mdtraj.Topology`, default is None
        If provided, residue labels wil be auto-generated from
        here
    shortenAAs : boolean, default is True
        If :obj:`top` is not None, use "E50" rather than "GLU50"
    aa_offset : int, default is 0
        Add this number to the resSeq value
    highlight_residxs : iterable of ints, default is None
        In case you don't want to construct a whole
        color list for :obj:`colors`, you can simply
        input a subset of :obj:`res_idxs` here and
        they will be shown in red.
    aura : iterable, default is None
        Scalar array, indexed with residue indices,
        e.g. RMSF, SASA, conv. degree...
        It will be drawn as an *aura* around the
        flareplot.

    Returns
    -------
    iax, xy, outdict

    outdict: dict
         Contains :obj:`matplotlib` objects
         like the dots and their labels:
         "fragment_labels", "dot_labels",
         "dots", "SS_labels", "r",
    """
    debug = False

    xy = _futils.cartify_fragments(fragments,
                                   r=r,
                                   angle_offset=angle_offset,
                                   padding=padding)
    xy += center

    n_positions = len(
        xy) + padding[0] + len(fragments) * padding[1] + padding[2]

    # TODO review variable names
    # TODO this color mess needs to be cleaned up
    residues_to_plot_as_dots = _np.hstack(fragments)
    col_list = _futils.col_list_from_input_and_fragments(colors,
                                                         fragments,
                                                         alpha=.80)
    if iax is None:
        _plt.figure(figsize=(panelsize, panelsize), tight_layout=True)
        iax = _plt.gca()
    else:
        if not iax.figure.get_tight_layout():
            print(
                "The passed figure was not instantiated with tight_layout=True\n"
                "This may lead to some errors in the flareplot fontsizes.")
    # Do this first to have an idea of the points per axis unit necessary for the plot
    iax.set_xlim([center[0] - r, center[0] + r])
    iax.set_ylim([center[1] - r, center[1] + r])
    iax.set_aspect('equal')

    # Create a map
    residx2markeridx = _futils.value2position_map(residues_to_plot_as_dots)

    # Plot!
    running_r_pad = 0
    if markersize is None:
        dot_radius = r * _np.sin(_np.pi / n_positions)
        dot_radius_in_pts = dot_radius * _points2dataunits(iax).mean()
        if dot_radius_in_pts < 1.5:
            print(
                ValueError(
                    "Drawing this many of residues (%u) in "
                    "a panel %3.1f inches wide/high "
                    "forces too small dotsizes and fontsizes.\n"
                    "If crowding effects "
                    "occur, either reduce the number of residues or increase "
                    "the panel size" % (n_positions, panelsize)))
        if not arc:
            #TODO replace this with a call to RegularPolyCollection
            CPs = [
                _CP(ixy,
                    radius=dot_radius,
                    facecolor=col_list[ii],
                    edgecolor=None,
                    zorder=10) for ii, ixy in enumerate(xy)
            ]
            [iax.add_artist(iCP) for iCP in CPs]
            running_r_pad += dot_radius
        else:
            lw = r * .05 * _points2dataunits(
                iax).mean()  #5% of the arc linewidth)
            _futils.draw_arcs(
                fragments,
                iax,
                colors=[col_list[ifrag[0]] for ifrag in fragments],
                center=center,
                r=2 * r,  # don't understand this 2 here
                angle_offset=angle_offset,
                padding=padding,
                lw=2 * lw)
            running_r_pad += 4 * lw
    else:
        raise NotImplementedError
        #iax.scatter(xy[:, 0], xy[:, 1], c=col_list, s=10, zorder=10)

    # After the dots have been plotted,
    # we use their radius as in points
    # as approximate fontsize
    if fontsize is None:
        opt_h_in_pts = dot_radius_in_pts
        fontsize = opt_h_in_pts
    else:
        raise NotImplementedError

    n_max = 100
    maxlen = 1
    labels = []
    if textlabels:
        if isinstance(textlabels, bool):
            replacement_labels = None
        else:
            # Interpret the textlabels as replacements
            replacement_labels = textlabels
        overlap = True
        counter = 0
        while overlap and counter < n_max:
            running_r_pad += dot_radius * maxlen / 2  # This is a fudge.
            # The problem is that re-scaling at a later point might induce overlap
            # in this labels again, so I am anticipating that by adding some extra padding here
            [ilab.remove() for ilab in labels]
            labels = add_fragmented_residue_labels(
                fragments,
                iax,
                fontsize,
                center=center,
                r=r + running_r_pad,
                angle_offset=angle_offset,
                padding=padding,
                highlight_residxs=highlight_residxs,
                top=top,
                aa_offset=aa_offset,
                replacement_labels=replacement_labels)
            lab_lenths = [len(itext.get_text()) for itext in labels]
            idx_longest_label = _np.argmax(lab_lenths)
            maxlen = _np.max(lab_lenths)
            bad_txt_bb = labels[idx_longest_label].get_window_extent(
                renderer=iax.figure.canvas.get_renderer())
            bad_dot_bb = CPs[idx_longest_label].get_window_extent(
                renderer=iax.figure.canvas.get_renderer())
            overlap = bad_txt_bb.overlaps(bad_dot_bb)
            counter += 1

        assert not overlap, ValueError(
            "Tried to 'un'-overlap textlabels and residue markers %u times without success"
            % n_max)

        running_r_pad += dot_radius * maxlen / 2
    if debug:
        iax.add_artist(
            _plt.Circle(center,
                        radius=r + running_r_pad,
                        ec='r',
                        fc=None,
                        fill=False,
                        zorder=10))

    # Do we have SS dictionaries
    ss_labels = []
    if ss_array is not None:
        overlap = True
        counter = 0
        ss_colors = [
            _futils._SS2vmdcol[ss_array[res_idx]]
            for res_idx in residues_to_plot_as_dots
        ]
        while overlap and counter < n_max:
            running_r_pad += dot_radius * 2
            [ilab.remove() for ilab in ss_labels]
            ss_labels = add_fragmented_residue_labels(
                fragments,
                iax,
                fontsize * 2,
                center=center,
                r=r + running_r_pad,
                angle_offset=angle_offset,
                padding=padding,
                shortenAAs=shortenAAs,
                highlight_residxs=highlight_residxs,
                top=top,
                aa_offset=aa_offset,
                replacement_labels={
                    ii: ss_array[ii].replace("NA", "").replace("E", "B")
                    for ii in residues_to_plot_as_dots
                },
                colors=[
                    _futils._make_color_transparent(col, .8, "w")
                    for col in ss_colors
                ],
                weight="bold")
            idx_longest_label = _np.argmax(
                [len(itext.get_text()) for itext in ss_labels])
            # We're doing this "by hand" here because it's just two or at most 3 offenders
            bad_ss_bb = ss_labels[idx_longest_label].get_window_extent(
                renderer=iax.figure.canvas.get_renderer())
            bad_dot_bb = CPs[idx_longest_label].get_window_extent(
                renderer=iax.figure.canvas.get_renderer())
            overlap = bad_ss_bb.overlaps(bad_dot_bb)
            if len(labels) > 0:
                overlap_w_text = bad_ss_bb.overlaps(
                    labels[idx_longest_label].get_window_extent(
                        renderer=iax.figure.canvas.get_renderer()))
                overlap = _np.any([overlap, overlap_w_text])
            counter += 1

        running_r_pad += dot_radius
    if debug:
        iax.add_artist(
            _plt.Circle(center,
                        radius=r + running_r_pad,
                        ec='b',
                        fc=None,
                        fill=False,
                        zorder=10))
    auras = []
    if aura is not None:
        if debug:
            iax.add_artist(
                _plt.Circle([0, 0],
                            r + running_r_pad,
                            ec="k",
                            alpha=.25,
                            zorder=-100))
        #TODO running pad is somehow mis-calculated here
        auras, nr = _futils.add_aura(xy,
                                     aura[_np.hstack(fragments)],
                                     iax,
                                     r + running_r_pad + dot_radius,
                                     [len(fr) for fr in fragments],
                                     subtract_baseline=False)
        running_r_pad += r * _signature(
            _futils.add_aura).parameters["width"].default + dot_radius
        if aura is not None:
            if debug:
                iax.add_artist(
                    _plt.Circle([0, 0],
                                r + running_r_pad,
                                ec="k",
                                alpha=.25,
                                zorder=-100))
                iax.add_artist(
                    _plt.Circle([0, 0], nr, ec="g", alpha=.25, zorder=-100))

    # Do we have names?
    frag_labels = None
    if fragment_names is not None:
        span = (2 * (r + running_r_pad))
        frag_fontsize_in_aus = span / 6 * 1 / 5  # (average_word_length, fraction of panel space)
        frag_fontsize_in_pts = frag_fontsize_in_aus * _points2dataunits(
            iax).mean()
        frag_labels = _futils.add_fragment_labels(
            fragments,
            [replace4latex(ifrag) for ifrag in fragment_names],
            iax,
            angle_offset=angle_offset,
            padding=padding,
            #xy,
            #residx2markeridx,
            fontsize=frag_fontsize_in_pts,
            center=center,
            r=r + running_r_pad)
        # First un-overlapp the labels themselves
        _futils.un_overlap_via_fontsize(frag_labels)
        frag_fontsize_in_pts = frag_labels[0].get_size()

        # Then find the overlappers among existing labels (to avoid using all labels unnecessarily)
        foverlappers = _futils.overlappers(frag_labels,
                                           CPs + labels + ss_labels + auras)
        counter = 0
        while any(_futils.overlappers(frag_labels,
                                      foverlappers)) and counter < n_max:
            [fl.remove() for fl in frag_labels]
            running_r_pad += frag_fontsize_in_pts / _points2dataunits(
                iax).mean()
            frag_labels = _futils.add_fragment_labels(
                fragments,
                [replace4latex(ifrag) for ifrag in fragment_names],
                iax,  #xy,
                #residx2markeridx,
                angle_offset=angle_offset,
                padding=padding,
                fontsize=frag_fontsize_in_pts,
                center=center,
                r=r + running_r_pad)
            # print(counter, overlappers(frag_labels, foverlappers))
            counter += 1
            frag_fontsize_in_pts = frag_labels[0].get_size()

        running_r_pad += frag_fontsize_in_pts / _points2dataunits(iax).mean()
    iax.set_yticks([])
    iax.set_xticks([])
    iax.set_xlim(
        [center[0] - r - running_r_pad, center[0] + r + running_r_pad])
    iax.set_ylim(
        [center[1] - r - running_r_pad, center[1] + r + running_r_pad])
    return iax, xy, {
        "fragment_labels": frag_labels,
        "dot_labels": labels,
        "dots": CPs,
        "SS_labels": ss_labels,
        "r": r + running_r_pad
    }
Exemplo n.º 14
0
def generate_repr(
        method: _Union[_Constructor, _Initializer],
        *,
        argument_serializer: _ArgumentSerializer = _serializers.simple,
        field_seeker: _FieldSeeker = _seekers.simple,
        prefer_keyword: bool = False,
        skip_defaults: bool = False,
        with_module_name: bool = False) -> _Map[_Domain, str]:
    """
    Generates ``__repr__`` method based on constructor/initializer parameters.

    We are assuming that no parameters data
    get thrown away during instance creation,
    so we can re-create it after.

    :param method:
        constructor/initializer method
        which parameters will be used in resulting representation.
    :param argument_serializer: function that serializes argument to string.
    :param field_seeker:
        function that re-creates parameter value
        based on class instance and name.
    :param prefer_keyword:
        flag that specifies
        if positional-or-keyword parameters should be outputted
        as keyword ones when possible.
    :param skip_defaults:
        flag that specifies
        if optional parameters with default arguments should be skipped.
    :param with_module_name:
        flag that specifies if module name should be added.

    >>> from reprit.base import generate_repr
    >>> class Person:
    ...     def __init__(self, name, *, address=None):
    ...         self.name = name
    ...         self.address = address
    ...     __repr__ = generate_repr(__init__,
    ...                              skip_defaults=True)
    >>> Person('Adrian')
    Person('Adrian')
    >>> Person('Mary', address='Somewhere on Earth')
    Person('Mary', address='Somewhere on Earth')
    >>> class ScoreBoard:
    ...     def __init__(self, first, *rest):
    ...         self.first = first
    ...         self.rest = rest
    ...     __repr__ = generate_repr(__init__,
    ...                              prefer_keyword=True)
    >>> ScoreBoard(1)
    ScoreBoard(first=1)
    >>> ScoreBoard(1, 40)
    ScoreBoard(1, 40)
    >>> class Student:
    ...     def __init__(self, name, group):
    ...         self.name = name
    ...         self.group = group
    ...     __repr__ = generate_repr(__init__,
    ...                              with_module_name=True)
    >>> Student('Kira', 132)
    reprit.base.Student('Kira', 132)
    >>> Student('Naomi', 248)
    reprit.base.Student('Naomi', 248)
    >>> from reprit import seekers
    >>> class Account:
    ...     def __init__(self, id_, *, balance=0):
    ...         self.id = id_
    ...         self.balance = balance
    ...     __repr__ = generate_repr(__init__,
    ...                              field_seeker=seekers.complex_)
    >>> Account(1)
    Account(1, balance=0)
    >>> Account(100, balance=-10)
    Account(100, balance=-10)
    >>> import json
    >>> class Object:
    ...     def __init__(self, value):
    ...         self.value = value
    ...     def serialized(self):
    ...         return json.dumps(self.value)
    ...     @classmethod
    ...     def from_serialized(cls, serialized):
    ...         return cls(json.loads(serialized))
    ...     __repr__ = generate_repr(from_serialized)
    >>> Object.from_serialized('0')
    Object.from_serialized('0')
    >>> Object.from_serialized('{"key": "value"}')
    Object.from_serialized('{"key": "value"}')
    """
    if with_module_name:

        def to_class_name(cls: type) -> str:
            return cls.__module__ + '.' + cls.__qualname__
    else:

        def to_class_name(cls: type) -> str:
            return cls.__qualname__

    unwrapped_method = (method.__func__ if isinstance(
        method, (classmethod, staticmethod)) else method)
    method_name = unwrapped_method.__name__
    parameters = OrderedDict(_signature(unwrapped_method).parameters)

    if method_name in ('__init__', '__new__'):
        # remove `cls`/`self`
        parameters.popitem(0)

        def __repr__(self: _Domain) -> str:
            return (to_class_name(type(self)) + '(' +
                    ', '.join(to_arguments_strings(self)) + ')')
    else:
        if isinstance(method, classmethod):
            # remove `cls`
            parameters.popitem(0)

        def __repr__(self: _Domain) -> str:
            return (to_class_name(type(self)) + '.' + method_name + '(' +
                    ', '.join(to_arguments_strings(self)) + ')')

    variadic_positional = next(
        (parameter for parameter in parameters.values()
         if parameter.kind is _ParameterKind.VAR_POSITIONAL), None)
    to_keyword_string = '{}={}'.format

    def to_arguments_strings(object_: _Domain) -> _Iterable[str]:
        variadic_positional_unset = (
            variadic_positional is None
            or not field_seeker(object_, variadic_positional.name))
        positional_or_keyword_is_keyword = (prefer_keyword
                                            and variadic_positional_unset)
        for name, parameter in parameters.items():
            field = field_seeker(object_, name)
            if isinstance(field, _MethodType) and field.__self__ is object_:
                field = field()
            kind = parameter.kind
            show_parameter = (not skip_defaults
                              or field is not parameter.default or
                              (not variadic_positional_unset and
                               (kind is _ParameterKind.POSITIONAL_ONLY or
                                kind is _ParameterKind.POSITIONAL_OR_KEYWORD)))
            if show_parameter:
                if kind is _ParameterKind.POSITIONAL_ONLY:
                    yield argument_serializer(field)
                elif kind is _ParameterKind.POSITIONAL_OR_KEYWORD:
                    yield (to_keyword_string(name, argument_serializer(field))
                           if positional_or_keyword_is_keyword else
                           argument_serializer(field))
                elif kind is _ParameterKind.VAR_POSITIONAL:
                    yield from ((argument_serializer(field), )
                                # we don't want to exhaust iterator
                                if isinstance(field, abc.Iterator) else map(
                                    argument_serializer, field))
                elif kind is _ParameterKind.KEYWORD_ONLY:
                    yield to_keyword_string(name, argument_serializer(field))
                else:
                    yield from map(to_keyword_string, field.keys(),
                                   map(argument_serializer, field.values()))
            elif (not positional_or_keyword_is_keyword
                  and (kind is _ParameterKind.POSITIONAL_ONLY
                       or kind is _ParameterKind.POSITIONAL_OR_KEYWORD)):
                positional_or_keyword_is_keyword = True

    return __repr__
Exemplo n.º 15
0
 def decorator(overriden: T) -> T:
     overriden.__doc__ = original.__doc__
     overriden.__signature__ = _signature(original)  # type: ignore
     return overriden
Exemplo n.º 16
0
 def decorator(overriden):
     overriden.__doc__ = original.__doc__
     overriden.__signature__ = _signature(original)
     return overriden
Exemplo n.º 17
0
def _delayed_init_wrapper(cls, **infer_delayed_dict):
    """
    :param cls: type
    class to wrap
    :param infer_delayed_dict: dict
    Keys are the arguments that are inferred only at first forward.
    The values are the functions that get the arguments based on the input to forward.
    :return: type
    """
    assert issubclass(cls, old.Module)
    sig = _signature(cls)

    class DelayedInitClass(cls):

        def __init__(self, *args, init_tensor=None, **kwargs):
            # save kwargs for delayed init
            self.kwargs = kwargs

            # if positional args are specified, get corresponding names and also save in self.kwargs
            # positional only arguments are not supported
            if len(args) > 0:
                parameters = sig.parameters
                # get available argument names
                available_argnames = []
                for argname in list(parameters.keys()):
                    if parameters[argname].kind == _POSITIONAL_OR_KEYWORD:
                        available_argnames.append(argname)
                    else:
                        break

                assert len(available_argnames) >= len(args), \
                    f'Too many positional arguments! {cls.__name__} only takes {len(available_argnames)} ' \
                    f'positional arguments but got {len(args)}.'

                argnames = available_argnames[:len(args)]
                for arg, name in zip(args, argnames):
                    assert name not in kwargs, f'Argument {name} specified both as positional and keyword argument.'
                    kwargs[name] = arg

            # if all arguments that could be inferred later are already specified, do the full init now
            if all([kwargs.get(arg, INIT_DELAYED) is not INIT_DELAYED
                    for arg in infer_delayed_dict]):
                self.do_init()

            # if an in_tensor is given, call forward now to infer unassigned arguments
            if init_tensor is not None:
                with _torch.no_grad():
                    self(init_tensor)

        def __call__(self, *input):
            # infer unassigned arguments from input
            for arg, infer_arg in infer_delayed_dict.items():
                if self.kwargs.get(arg, INIT_DELAYED) is INIT_DELAYED:
                    self.kwargs[arg] = infer_arg(*input)

            # do the init, using the updated kwargs
            self.do_init()

            # as the class of self is now cls, this is now the usual call of the base class
            return self.__call__(*input)

        def do_init(self):
            # call the init of the base class
            super(DelayedInitClass, self).__init__(**self.kwargs)

            # clean up: delete kwargs object
            del self.kwargs

            # aet the class to the base class. This should remove all traces of this class ever existing.
            assert len(self.__class__.__bases__) == 1, f'{self.__class__.__bases__}'
            self.__class__ = self.__class__.__bases__[0]

        def __repr__(self):
            # repr is similar of that for nn.Module, showing all kwargs including those left unassigned
            result = f'{cls.__name__}('
            for arg, value in self.kwargs.items():
                result += f'{arg}={value}, '
            for arg in infer_delayed_dict:
                if arg not in self.kwargs:
                    result += f'{arg} unassigned, '
            result = result[:-2] + ')'
            return result

    # use docstring and __init__ signature of wrapped class
    DelayedInitClass.__doc__ = cls.__doc__
    # TODO: add init_tensor to signature
    DelayedInitClass.__signature__ = sig

    return DelayedInitClass