Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maximum recursion depth crash with pyreverse -S option #3602

Open
glefebvr opened this issue May 7, 2020 · 8 comments
Open

Maximum recursion depth crash with pyreverse -S option #3602

glefebvr opened this issue May 7, 2020 · 8 comments
Labels
Bug 🪲 Crash 💥 A bug that makes pylint crash Needs PR This issue is accepted, sufficiently specified and now needs an implementation performance pyreverse Related to pyreverse component

Comments

@glefebvr
Copy link

glefebvr commented May 7, 2020

Steps to reproduce

Given this filen named test.py:

import numpy as np

class ExempleClass :
  typ = np.uint32
  
  def __init__(self,D):
      self.X = np.zeros( D, dtype=self.typ)

I run the command:
Pyreverse -o gv -A -my -S test.py

Current behavior

An error is raised. The error message is very very long. I paste here only the end of the message.

  File "C:\ProgramData\Anaconda3\lib\site-packages\pylint\pyreverse\diadefslib.py", line 121, in extract_classes
    self.extract_classes(node, anc_level, association_level - 1)
  [Previous line repeated 920 more times]
  File "C:\ProgramData\Anaconda3\lib\site-packages\pylint\pyreverse\diadefslib.py", line 115, in extract_classes
    self.add_class(klass_node)
  File "C:\ProgramData\Anaconda3\lib\site-packages\pylint\pyreverse\diadefslib.py", line 85, in add_class
    self.linker.visit(node)
  File "C:\ProgramData\Anaconda3\lib\site-packages\pylint\pyreverse\utils.py", line 217, in visit
    methods[0](node)
  File "C:\ProgramData\Anaconda3\lib\site-packages\pylint\pyreverse\inspector.py", line 170, in visit_classdef
    self.handle_assignattr_type(assignattr, node)
  File "C:\ProgramData\Anaconda3\lib\site-packages\pylint\pyreverse\inspector.py", line 235, in handle_assignattr_type
    values = set(node.infer())
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 132, in raise_if_nothing_inferred
    yield next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 96, in wrapped
    res = next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\bases.py", line 136, in _infer_stmts
    for inferred in stmt.infer(context=context):
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\util.py", line 160, in limit_inference
    yield from islice(iterator, size)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\context.py", line 113, in cache_generator
    for result in generator:
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 132, in raise_if_nothing_inferred
    yield next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 96, in wrapped
    res = next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\inference.py", line 227, in infer_call
    for callee in self.func.infer(context):
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\node_classes.py", line 357, in infer
    return self._explicit_inference(self, context, **kwargs)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\__init__.py", line 95, in _inference_tip_cached
    result = func(*args, **kwargs)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\brain\brain_numpy_ndarray.py", line 141, in infer_numpy_ndarray
    node = astroid.extract_node(ndarray)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\builder.py", line 421, in extract_node
    tree = parse(code, module_name=module_name)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\builder.py", line 278, in parse
    return builder.string_build(code, modname=module_name, path=path)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\builder.py", line 144, in string_build
    return self._post_build(module, "utf-8")
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\builder.py", line 158, in _post_build
    self.delayed_assattr(delayed)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\builder.py", line 225, in delayed_assattr
    for inferred in node.expr.infer():
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 132, in raise_if_nothing_inferred
    yield next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 96, in wrapped
    res = next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\bases.py", line 136, in _infer_stmts
    for inferred in stmt.infer(context=context):
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\util.py", line 160, in limit_inference
    yield from islice(iterator, size)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\context.py", line 113, in cache_generator
    for result in generator:
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 132, in raise_if_nothing_inferred
    yield next(generator)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 93, in wrapped
    generator = _func(node, context, **kwargs)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\inference.py", line 850, in infer_assign
    stmts = list(self.assigned_stmts(context=context))
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\protocols.py", line 323, in _arguments_infer_argname
    functype = self.parent.type
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 72, in __get__
    val = self.wrapped(inst)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\scoped_nodes.py", line 1461, in type
    for decorator in self.extra_decorators:
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 72, in __get__
    val = self.wrapped(inst)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\scoped_nodes.py", line 1424, in extra_decorators
    for assign in frame._get_assign_nodes():
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 34, in cached
    cache[func] = result = func(*args, **kwargs)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\scoped_nodes.py", line 2927, in _get_assign_nodes
    return list(itertools.chain.from_iterable(children_assign_nodes))
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\scoped_nodes.py", line 2925, in <genexpr>
    child_node._get_assign_nodes() for child_node in self.body
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 34, in cached
    cache[func] = result = func(*args, **kwargs)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\mixins.py", line 153, in _get_assign_nodes
    return list(itertools.chain.from_iterable(children_assign_nodes))
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\mixins.py", line 151, in <genexpr>
    for child_node in block
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 34, in cached
    cache[func] = result = func(*args, **kwargs)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\node_classes.py", line 1953, in _get_assign_nodes
    return [self] + list(self.value._get_assign_nodes())
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\decorators.py", line 28, in cached
    cache = getattr(instance, "__cache", None)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\node_classes.py", line 2575, in __getattr__
    return super().__getattr__(name)
  File "C:\ProgramData\Anaconda3\lib\site-packages\astroid\bases.py", line 113, in __getattr__
    return getattr(self._proxied, name)
RecursionError: maximum recursion depth exceeded while calling a Python object

Expected behavior

A generation of a file classes.gv

pylint --version output

pylint 2.5.0
astroid 2.4.0
Python 3.7.3 (default, Apr 24 2019, 15:29:51) [MSC v.1915 64 bit (AMD64)]

@PCManticore PCManticore added Bug 🪲 Crash 💥 A bug that makes pylint crash labels May 28, 2020
@PCManticore PCManticore changed the title Maximum recursion depth Maximum recursion depth with pyreverse May 28, 2020
@Pierre-Sassoulas
Copy link
Member

Pierre-Sassoulas commented Jan 1, 2021

Look like it's the first issue opened for this recursion bug and the following one are:
#3986, #3985, #3982, #3977, #3952, #3913, #3836

A possible temporary solution to the problem was proposed here: #3836 (comment)

turns out this is a genuinely large recursion problem, and can be gotten around by putting the following in your pylintrc file:
init-hook='import sys; sys.setrecursionlimit(3 * sys.getrecursionlimit())'

@Pierre-Sassoulas
Copy link
Member

Pierre-Sassoulas commented Mar 7, 2021

I just checked with pylint 2.7 astroid 2.5, it's not crashing but it's taking really fucking long (more than 10mn and counting) for parsing 5 line of code.

Edit: OOM killed 15mn in. [1] 3619 killed pyreverse -o gv -A -my -S a.py

@semajson
Copy link

I have created a .pylintrc file and added init-hook='import sys; sys.setrecursionlimit(3 * sys.getrecursionlimit())', however my pyreverse command still fails at the same point.

In fact, when I set it to init-hook='assert False', my pyreverse command still failed the same way. I suspect that the .pylintrc file isn't being used for pyreverse commands. Perhaps that could be a fix here, allowing pylintrc files to be to be used for pyreverse commands (perhaps by having adding a --rcfile argument to the pyreverse command)?

@DudeNr33
Copy link
Collaborator

Could you please share the versions of pylint and astroid you are using?
Ideally with a minimum working example to reproduce the issue. Thank you!

@Pierre-Sassoulas
Copy link
Member

On the latest main (2.14.0-b1) with the following a.py file:

import numpy as np

class ExempleClass :
  typ = np.uint32
  
  def __init__(self,D):
      self.X = np.zeros( D, dtype=self.typ)

running pyreverse -o gv -A -my -S a.py ends up in OOM kill.

@DudeNr33
Copy link
Collaborator

The offending option in this case is -S ("show recursively all associated off all associated classes").
The problem is with self.X = np.zeros(D, dtype=self.typ).
It is possible to limit the levels of association by using "-s2" (lower case -s, "show <association_level> levels of associated classes not in "). With this you end up with this diagram:

classDiagram
  class ExempleClass {
    X
    typ
  }
  class ndarray {
    T
    base : NoneType
    ctypes : NoneType
    data : NoneType
    dtype : NoneType
    flags : NoneType
    flat
    imag
    itemsize : NoneType
    nbytes : NoneType
    ndim : NoneType
    real
    shape
    size : NoneType
    strides : NoneType
    all(axis, out, keepdims)
    any(axis, out, keepdims)
    argmax(axis, out)
    argmin(axis, out)
    argpartition(kth, axis, kind, order)
    argsort(axis, kind, order)
    astype(dtype, order, casting, subok, copy)
    byteswap(inplace)
    choose(choices, out, mode)
    clip(min, max, out)
    compress(condition, axis, out)
    conj()
    conjugate()
    copy(order)
    cumprod(axis, dtype, out)
    cumsum(axis, dtype, out)
    diagonal(offset, axis1, axis2)
    dot(b, out)
    dump(file)
    dumps()
    fill(value)
    flatten(order)
    getfield(dtype, offset)
    item()
    itemset()
    max(axis, out)
    mean(axis, dtype, out, keepdims)
    min(axis, out, keepdims)
    newbyteorder(new_order)
    nonzero()
    partition(kth, axis, kind, order)
    prod(axis, dtype, out, keepdims)
    ptp(axis, out)
    put(indices, values, mode)
    ravel(order)
    repeat(repeats, axis)
    reshape(shape, order)
    resize(new_shape, refcheck)
    round(decimals, out)
    searchsorted(v, side, sorter)
    setfield(val, dtype, offset)
    setflags(write, align, uic)
    sort(axis, kind, order)
    squeeze(axis)
    std(axis, dtype, out, ddof, keepdims)
    sum(axis, dtype, out, keepdims)
    swapaxes(axis1, axis2)
    take(indices, axis, out, mode)
    tobytes(order)
    tofile(fid, sep, format)
    tolist()
    tostring(order)
    trace(offset, axis1, axis2, dtype, out)
    transpose()
    var(axis, dtype, out, ddof, keepdims)
    view(dtype, type)
  }
  class ndarray {
    T : ndarray
    base : NoneType
    ctypes : NoneType
    data : NoneType
    dtype : NoneType
    flags : NoneType
    flat : ndarray
    imag : ndarray
    itemsize : NoneType
    nbytes : NoneType
    ndim : NoneType
    real : ndarray
    shape : ndarray
    size : NoneType
    strides : NoneType
    all(axis, out, keepdims)
    any(axis, out, keepdims)
    argmax(axis, out)
    argmin(axis, out)
    argpartition(kth, axis, kind, order)
    argsort(axis, kind, order)
    astype(dtype, order, casting, subok, copy)
    byteswap(inplace)
    choose(choices, out, mode)
    clip(min, max, out)
    compress(condition, axis, out)
    conj()
    conjugate()
    copy(order)
    cumprod(axis, dtype, out)
    cumsum(axis, dtype, out)
    diagonal(offset, axis1, axis2)
    dot(b, out)
    dump(file)
    dumps()
    fill(value)
    flatten(order)
    getfield(dtype, offset)
    item()
    itemset()
    max(axis, out)
    mean(axis, dtype, out, keepdims)
    min(axis, out, keepdims)
    newbyteorder(new_order)
    nonzero()
    partition(kth, axis, kind, order)
    prod(axis, dtype, out, keepdims)
    ptp(axis, out)
    put(indices, values, mode)
    ravel(order)
    repeat(repeats, axis)
    reshape(shape, order)
    resize(new_shape, refcheck)
    round(decimals, out)
    searchsorted(v, side, sorter)
    setfield(val, dtype, offset)
    setflags(write, align, uic)
    sort(axis, kind, order)
    squeeze(axis)
    std(axis, dtype, out, ddof, keepdims)
    sum(axis, dtype, out, keepdims)
    swapaxes(axis1, axis2)
    take(indices, axis, out, mode)
    tobytes(order)
    tofile(fid, sep, format)
    tolist()
    tostring(order)
    trace(offset, axis1, axis2, dtype, out)
    transpose()
    var(axis, dtype, out, ddof, keepdims)
    view(dtype, type)
  }
  class ndarray {
    T : ndarray
    base : NoneType
    ctypes : NoneType
    data : NoneType
    dtype : NoneType
    flags : NoneType
    flat : ndarray
    imag : ndarray
    itemsize : NoneType
    nbytes : NoneType
    ndim : NoneType
    real : ndarray
    shape : ndarray
    size : NoneType
    strides : NoneType
    all(axis, out, keepdims)
    any(axis, out, keepdims)
    argmax(axis, out)
    argmin(axis, out)
    argpartition(kth, axis, kind, order)
    argsort(axis, kind, order)
    astype(dtype, order, casting, subok, copy)
    byteswap(inplace)
    choose(choices, out, mode)
    clip(min, max, out)
    compress(condition, axis, out)
    conj()
    conjugate()
    copy(order)
    cumprod(axis, dtype, out)
    cumsum(axis, dtype, out)
    diagonal(offset, axis1, axis2)
    dot(b, out)
    dump(file)
    dumps()
    fill(value)
    flatten(order)
    getfield(dtype, offset)
    item()
    itemset()
    max(axis, out)
    mean(axis, dtype, out, keepdims)
    min(axis, out, keepdims)
    newbyteorder(new_order)
    nonzero()
    partition(kth, axis, kind, order)
    prod(axis, dtype, out, keepdims)
    ptp(axis, out)
    put(indices, values, mode)
    ravel(order)
    repeat(repeats, axis)
    reshape(shape, order)
    resize(new_shape, refcheck)
    round(decimals, out)
    searchsorted(v, side, sorter)
    setfield(val, dtype, offset)
    setflags(write, align, uic)
    sort(axis, kind, order)
    squeeze(axis)
    std(axis, dtype, out, ddof, keepdims)
    sum(axis, dtype, out, keepdims)
    swapaxes(axis1, axis2)
    take(indices, axis, out, mode)
    tobytes(order)
    tofile(fid, sep, format)
    tolist()
    tostring(order)
    trace(offset, axis1, axis2, dtype, out)
    transpose()
    var(axis, dtype, out, ddof, keepdims)
    view(dtype, type)
  }
  class ndarray {
    T : ndarray
    base : NoneType
    ctypes : NoneType
    data : NoneType
    dtype : NoneType
    flags : NoneType
    flat : ndarray
    imag : ndarray
    itemsize : NoneType
    nbytes : NoneType
    ndim : NoneType
    real : ndarray
    shape : ndarray
    size : NoneType
    strides : NoneType
    all(axis, out, keepdims)
    any(axis, out, keepdims)
    argmax(axis, out)
    argmin(axis, out)
    argpartition(kth, axis, kind, order)
    argsort(axis, kind, order)
    astype(dtype, order, casting, subok, copy)
    byteswap(inplace)
    choose(choices, out, mode)
    clip(min, max, out)
    compress(condition, axis, out)
    conj()
    conjugate()
    copy(order)
    cumprod(axis, dtype, out)
    cumsum(axis, dtype, out)
    diagonal(offset, axis1, axis2)
    dot(b, out)
    dump(file)
    dumps()
    fill(value)
    flatten(order)
    getfield(dtype, offset)
    item()
    itemset()
    max(axis, out)
    mean(axis, dtype, out, keepdims)
    min(axis, out, keepdims)
    newbyteorder(new_order)
    nonzero()
    partition(kth, axis, kind, order)
    prod(axis, dtype, out, keepdims)
    ptp(axis, out)
    put(indices, values, mode)
    ravel(order)
    repeat(repeats, axis)
    reshape(shape, order)
    resize(new_shape, refcheck)
    round(decimals, out)
    searchsorted(v, side, sorter)
    setfield(val, dtype, offset)
    setflags(write, align, uic)
    sort(axis, kind, order)
    squeeze(axis)
    std(axis, dtype, out, ddof, keepdims)
    sum(axis, dtype, out, keepdims)
    swapaxes(axis1, axis2)
    take(indices, axis, out, mode)
    tobytes(order)
    tofile(fid, sep, format)
    tolist()
    tostring(order)
    trace(offset, axis1, axis2, dtype, out)
    transpose()
    var(axis, dtype, out, ddof, keepdims)
    view(dtype, type)
  }
  class ndarray {
    T : ndarray
    base : NoneType
    ctypes : NoneType
    data : NoneType
    dtype : NoneType
    flags : NoneType
    flat : ndarray
    imag : ndarray
    itemsize : NoneType
    nbytes : NoneType
    ndim : NoneType
    real : ndarray
    shape : ndarray
    size : NoneType
    strides : NoneType
    all(axis, out, keepdims)
    any(axis, out, keepdims)
    argmax(axis, out)
    argmin(axis, out)
    argpartition(kth, axis, kind, order)
    argsort(axis, kind, order)
    astype(dtype, order, casting, subok, copy)
    byteswap(inplace)
    choose(choices, out, mode)
    clip(min, max, out)
    compress(condition, axis, out)
    conj()
    conjugate()
    copy(order)
    cumprod(axis, dtype, out)
    cumsum(axis, dtype, out)
    diagonal(offset, axis1, axis2)
    dot(b, out)
    dump(file)
    dumps()
    fill(value)
    flatten(order)
    getfield(dtype, offset)
    item()
    itemset()
    max(axis, out)
    mean(axis, dtype, out, keepdims)
    min(axis, out, keepdims)
    newbyteorder(new_order)
    nonzero()
    partition(kth, axis, kind, order)
    prod(axis, dtype, out, keepdims)
    ptp(axis, out)
    put(indices, values, mode)
    ravel(order)
    repeat(repeats, axis)
    reshape(shape, order)
    resize(new_shape, refcheck)
    round(decimals, out)
    searchsorted(v, side, sorter)
    setfield(val, dtype, offset)
    setflags(write, align, uic)
    sort(axis, kind, order)
    squeeze(axis)
    std(axis, dtype, out, ddof, keepdims)
    sum(axis, dtype, out, keepdims)
    swapaxes(axis1, axis2)
    take(indices, axis, out, mode)
    tobytes(order)
    tofile(fid, sep, format)
    tolist()
    tostring(order)
    trace(offset, axis1, axis2, dtype, out)
    transpose()
    var(axis, dtype, out, ddof, keepdims)
    view(dtype, type)
  }
  class ndarray {
    T : ndarray
    base : NoneType
    ctypes : NoneType
    data : NoneType
    dtype : NoneType
    flags : NoneType
    flat : ndarray
    imag : ndarray
    itemsize : NoneType
    nbytes : NoneType
    ndim : NoneType
    real : ndarray
    shape : ndarray
    size : NoneType
    strides : NoneType
    all(axis, out, keepdims)
    any(axis, out, keepdims)
    argmax(axis, out)
    argmin(axis, out)
    argpartition(kth, axis, kind, order)
    argsort(axis, kind, order)
    astype(dtype, order, casting, subok, copy)
    byteswap(inplace)
    choose(choices, out, mode)
    clip(min, max, out)
    compress(condition, axis, out)
    conj()
    conjugate()
    copy(order)
    cumprod(axis, dtype, out)
    cumsum(axis, dtype, out)
    diagonal(offset, axis1, axis2)
    dot(b, out)
    dump(file)
    dumps()
    fill(value)
    flatten(order)
    getfield(dtype, offset)
    item()
    itemset()
    max(axis, out)
    mean(axis, dtype, out, keepdims)
    min(axis, out, keepdims)
    newbyteorder(new_order)
    nonzero()
    partition(kth, axis, kind, order)
    prod(axis, dtype, out, keepdims)
    ptp(axis, out)
    put(indices, values, mode)
    ravel(order)
    repeat(repeats, axis)
    reshape(shape, order)
    resize(new_shape, refcheck)
    round(decimals, out)
    searchsorted(v, side, sorter)
    setfield(val, dtype, offset)
    setflags(write, align, uic)
    sort(axis, kind, order)
    squeeze(axis)
    std(axis, dtype, out, ddof, keepdims)
    sum(axis, dtype, out, keepdims)
    swapaxes(axis1, axis2)
    take(indices, axis, out, mode)
    tobytes(order)
    tofile(fid, sep, format)
    tolist()
    tostring(order)
    trace(offset, axis1, axis2, dtype, out)
    transpose()
    var(axis, dtype, out, ddof, keepdims)
    view(dtype, type)
  }
  class uint32 {
  }
  ndarray --* ndarray : T
  ndarray --* ndarray : flat
  ndarray --* ndarray : imag
  ndarray --* ndarray : real
  ndarray --* ndarray : shape
  ndarray --* ExempleClass : X
  uint32 --* ExempleClass : typ

@Pierre-Sassoulas
Copy link
Member

Sounds like the issue could be closed if we put a lower default value when nothing is set ? It's easy to shoot oneself in the foot right now.

@DudeNr33
Copy link
Collaborator

Well, -S explicitly says recursively all associated classes.
It is not turned on by default (*), so I don't see a need to change this behaviour (the recursion error itself should be fixed of course).

(*) this is not entirely true, as using the --class option to generate a diagram of a specific class sets -ASmy as default.
But for normal invocations, i.e. pyreverse a.py, no associated classes outside the own project will be shown.

@Pierre-Sassoulas Pierre-Sassoulas added this to the 2.15.0 milestone Jul 17, 2022
@Pierre-Sassoulas Pierre-Sassoulas added the Needs PR This issue is accepted, sufficiently specified and now needs an implementation label Jul 21, 2022
@Pierre-Sassoulas Pierre-Sassoulas changed the title Maximum recursion depth with pyreverse Maximum recursion depth crash with pyreverse -S option Jul 21, 2022
@Pierre-Sassoulas Pierre-Sassoulas removed this from the 2.15.0 milestone Aug 25, 2022
@jacobtylerwalls jacobtylerwalls added the pyreverse Related to pyreverse component label May 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🪲 Crash 💥 A bug that makes pylint crash Needs PR This issue is accepted, sufficiently specified and now needs an implementation performance pyreverse Related to pyreverse component
Projects
None yet
Development

No branches or pull requests

6 participants