cdfmlr / pyflowchart

Python codes to Flowcharts

Home Page:https://pypi.org/project/pyflowchart/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for match-case statement: AttributeError: 'Unparser' object has no attribute '_Match'

bjoernma opened this issue · comments

As pyflowchart 0.3.1 used with python 3.11 and astunparse 1.6.3 throws an exception:
Traceback (most recent call last): File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main return _run_code(code, main_globals, None, File "/usr/lib/python3.10/runpy.py", line 86, in _run_code exec(code, run_globals) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/__main__.py", line 115, in <module> main(args.code_file, args.field, args.inner, args.output, args.no_simplify, args.conds_align) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/__main__.py", line 85, in main flowchart = Flowchart.from_code(code, File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/flowchart.py", line 102, in from_code p = parse(f, simplify=simplify, conds_align=conds_align) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 659, in parse node = ast_node_class(ast_object, **kwargs) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 137, in __init__ self.body_head, self.body_tails = self.parse_func_body(**kwargs) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 158, in parse_func_body p = parse(self.ast_object.body, **kwargs) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 659, in parse node = ast_node_class(ast_object, **kwargs) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 463, in __init__ OperationNode.__init__(self, operation=self.ast_to_source()) File "/home/bjorn/.local/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 33, in ast_to_source return astunparse.unparse(self.ast_object).strip() File "/home/bjorn/.local/lib/python3.10/site-packages/astunparse/__init__.py", line 13, in unparse Unparser(tree, file=v) File "/home/bjorn/.local/lib/python3.10/site-packages/astunparse/unparser.py", line 38, in __init__ self.dispatch(tree) File "/home/bjorn/.local/lib/python3.10/site-packages/astunparse/unparser.py", line 65, in dispatch meth = getattr(self, "_"+tree.__class__.__name__) AttributeError: 'Unparser' object has no attribute '_Match'

I looked into a solution. It now seems that replacing astunparse with the inbuild package ast package (see e.g. simonpercivall/astunparse#56 (comment)) doesnt throw this and works atm.

commented

Thanks for reporting. I'm on it!

commented

@bjoernma would you mind to provide some more detailed information about how you triggered the exception. I think I've fixed it (by following the solution you mentioned), but I haven't been able to reproduce the problem to test it. So I am not sure if there are any further bugs that prevent you from using PyFlowchart freely.

Able to replicate using python 3.10.11, pyflowchart 0.3.1, and astunparse 1.6.3 on a new Repl on Replit. Happened after adding a match-case block to my code. Traceback generated follows:

Traceback (most recent call last):
  File "/nix/store/xf54733x4chbawkh1qvy9i1i4mlscy1c-python3-3.10.11/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/nix/store/xf54733x4chbawkh1qvy9i1i4mlscy1c-python3-3.10.11/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/pyflowchart/__main__.py", line 115, in <module>
    main(args.code_file, args.field, args.inner, args.output, args.no_simplify, args.conds_align)
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/pyflowchart/__main__.py", line 85, in main
    flowchart = Flowchart.from_code(code,
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/pyflowchart/flowchart.py", line 102, in from_code
    p = parse(f, simplify=simplify, conds_align=conds_align)
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 659, in parse
    node = ast_node_class(ast_object, **kwargs)
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 463, in __init__
    OperationNode.__init__(self, operation=self.ast_to_source())
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/pyflowchart/ast_node.py", line 33, in ast_to_source
    return astunparse.unparse(self.ast_object).strip()
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/astunparse/__init__.py", line 13, in unparse
    Unparser(tree, file=v)
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/astunparse/unparser.py", line 38, in __init__
    self.dispatch(tree)
  File "/home/runner/Hollow-Knight-Charm-Build-Randomiser/.pythonlibs/lib/python3.10/site-packages/astunparse/unparser.py", line 65, in dispatch
    meth = getattr(self, "_"+tree.__class__.__name__)
AttributeError: 'Unparser' object has no attribute '_Match'

Thanks, @AzureArmageddon. There seem to be more jobs to do aside the compatibility of astunparse package to support the new match statement. I am considering literal convert the match-case back into the if-else statement OR a new kind of AstNode would be required to support it.

Since a multi-branch condition node is not supported by flowchart.js (See #13 for details), even though we have introduced a conds-align feature which may be useful, supporting for match could be really hard. 😭

As the match-case statement has been widely adopted today, I recommend that those encountering this problem consider a quick fix:

  • Make a copy of the code you want to create a flowchart for.
  • Rewrite your match-case code into an if-elif-else block.
  • Try running pyflowchart on the modified version.
  • The conds-align feature can help improve the readability of the generated flowchart.

Progress:

With my latest commit 12fcc03, PyFlowchart is now able to read & parse the match sentences (with Python 3.10+).

As mentioned before,

Since a multi-branch condition node is not supported by flowchart.js (See #13 for details), even though we have introduced a conds-align feature which may be useful, supporting for match could be really hard. 😭

I have no choice but to make each case in the match a condition node (looks like a if without else) and connect them one by one.

For example:

def test_match(a, b, c):
    if a > 0:
        match b:
            case 1:
                print('ab')
            case 2:
                print('abc')
                c = 1 + b + a
            case 3:
                print('nested match')
                match c:
                    case["a"]:
                        print('a')
                    case["a", *other_items]:
                        print('a and others')
                    case[*first_items, "d"] | (*first_items, "d"):
                        print('d is the last item')
            case _:
                print('abcd')
        end_of_match()
    else:
        alez()
    end_of_ifs()

Excuse me, this code as a example is a bit verbose (but it covers common use cases, I assume). So the generated flowchart is "a bit" long:

match-case

It's definitely not ideal. I have tried to make it more readable (by reusing the conds-align and connection direction features), but it backfired on me:

failed to align match-cases

No one like this bad-routed maze. So I decide to remove all this programic "beautify" features. Just leave it "a bit long".

So, in short, it would appear that astunparse's inability to parse match-case has been overcome, but flowchart.js is unable to handle multi-branching conditionals in the exact way we want so this temporary implementation must be used. Not ideal, but that means there is officially a new feature working! 🎉

Perhaps making an issue on flowchart.js would be appropriate. I don't suppose we can close this issue until flowchart.js makes changes (or mayhaps if an attractive alternative comes along as a viable replacement)?

Perhaps making an issue on flowchart.js would be appropriate.

There's an existing one that has persisted for years: adrai/flowchart.js#60.

or mayhaps if an attractive alternative comes along as a viable replacement

This is exactly what I am trying to.

I am considering mermaid.js as a potential alternative. Lately, I've favored mermaid.js over flowchart.js. However, it's not a straightforward shift due to PyFlowchart's initial design being closely tied to flowchart.js.

Adopting mermaid.js will undoubtedly cause significant disruptions to our stable compatibility, which I take pride in since its initial release in 2020 for Python 3.6/3.7.

So I'm still seeking a way to make a refactor that not destroy the current flowchart.js features, but allow us to add a support for mermaid.js. (Maybe the silver bullet here is the visitor pattern?)

Anyway, I'm releasing a preview (not well tested) version v0.4.0-alpha.4 that contains current progress for match-case support. pip install pyflowchart==0.4.0a4 to use it.

Keep this issue open for seeking further enhancements.

As pyflowchart 0.3.1 used with python 3.11 and astunparse 1.6.3 throws an exception:

Traceback (most recent call last):
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\flowchart.py", line 12, in
fc = pfc.parse(code)
^^^^^^^^^^^^^^^
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\new_env\Lib\site-packages\pyflowchart\ast_node.py", line 659, in parse
node = ast_node_class(ast_object, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\new_env\Lib\site-packages\pyflowchart\ast_node.py", line 463, in init
OperationNode.init(self, operation=self.ast_to_source())
^^^^^^^^^^^^^^^^^^^^
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\new_env\Lib\site-packages\pyflowchart\ast_node.py", line 33, in ast_to_source
return astunparse.unparse(self.ast_object).strip()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\new_env\Lib\site-packages\astunparse_init_.py", line 13, in unparse
Unparser(tree, file=v)
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\new_env\Lib\site-packages\astunparse\unparser.py", line 38, in init
self.dispatch(tree)
File "C:\Downloads\sc-navigator-model-generation-cp\APO Input Workflow\src\new_env\Lib\site-packages\astunparse\unparser.py", line 65, in dispatch
meth = getattr(self, "_"+tree.class.name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Unparser' object has no attribute '_str'. Did you mean: '_Str'?

How do I resolve this issue?

@kaasima10, it appears that your comment may not be related to this thread. Your issue will be traced in #32.