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 _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
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
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
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 ]
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 ['?']
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
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
def decorator(overriden: Callable[..., Any]) -> Callable[..., Any]: overriden.__doc__ = original.__doc__ overriden.__signature__ = _signature(original) # type: ignore return overriden
def signature(func, use_original=False): """Better signature function""" return _signature(func) if use_original else (getattr(func, '__signature__', None) or _signature(func))
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
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 }
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__
def decorator(overriden: T) -> T: overriden.__doc__ = original.__doc__ overriden.__signature__ = _signature(original) # type: ignore return overriden
def decorator(overriden): overriden.__doc__ = original.__doc__ overriden.__signature__ = _signature(original) return overriden
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