SuspensionError

Admin note: Moved to a new thread as it’s a separate question

Hello: :grinning_face_with_smiling_eyes:

Running the following in a Client Code Module (not a Form ):

# Inside (Client Code Module): utils_frontend.anvil_portable_classes
colNames = [item['name'] for item in app_tables.myTable.list_columns()]
MyRow = portable_tuple(namedtuple('MyRow', colNames))

emits this exception:

SuspensionError: Cannot call a function that blocks or suspends here.

Is this form problematic? Thank you!

I don’t see anything wrong with that line.
As far as I know a list comprehension should be able to use blocking code.

Perhaps this list comprehension is inside a function that is not able.
Where is that line?

Perhaps this can be solved as suggested on the main answer of this post by wrapping the blocking generator in a list(), something like this:

colNames = [item['name'] for item in list(app_tables.myTable.list_columns())]

or this:

list_columns = list(app_tables.myTable.list_columns())
colNames = [item['name'] for item in list_columns]

More details about blocking code are here: Anvil Docs | Client-Side Python


(Please create new questions when old questions already have an answer and that answer doesn’t apply to your new one)

1 Like

Okay, you may be onto something. While the code is in a Client Module, and not within Python function, it gets kicked off like this from the Backend (because it’s part of a portable class implementation):

# In a Server Module:
from ..utils_frontend.anvil_portable_classes import MyRow

[ ... snip ... ]

MyRow(**d) # Where 'd' is a simple dict()

(Note: I updated my original question with an additional statement).

So it’s more involved than I originally explained (not realizing). There’s some back-and-front interaction involved.

Is this module level code, executed when the module is imported?

Then making it lazy should work.
Something like this:

_col_names = {}

def col_names(table_name):
    if table_name not in _col_names:
        table = getattr(app_tables, table_name)
        columns = list(table.list_columns())
        col_names = [column['name'] for column in columns]
        _col_names[table_name] = col_names
    return _col_names[table_name]

This version can be used with any table. You can simplify it and make it work only with one table.

The point is (I think) you can’t execute blocking code during the import. Making it lazy you delay the execution of the blocking code at a later point, after the import has been completed.


My generic version could have a negative impact on the performance if you plan to call it multiple times with multiple tables, because it will trigger one round trip per table.

If that is the case, then it’s better to make one get_all_tables_columns server function that collects all the columns for all the tables that you are going to eventually need in one round trip.

Or a client side function that accepts a list of table names, calls a server function, the server function collects all the columns for all the tables and returns them all in one shot.

Paying so much attention to performance is a little bit against the Anvil philosophy, where being productive is more important than having the best performance possible, but I think it’s a piece information that can turn useful the day the performance become the bottleneck.

1 Like

First, thanks for the philosophy note. It makes sense to me – I’ve not been able to quickly make inroads on a high-value app until I discovered Anvil. :smiley:

The lazy load approach as well as one-round-trip to collect all table-column names needed are good strategies. Let me work on this (in a few days), and post back. Thank you!

Just jumping in here to say this isn’t true! Ordinary imports can block. Likewise, that code all seems fairly normal, so I’d suggest @nmvega work a bit more to narrow down where exactly the SuspensionError is occurring. Which line is it thrown from? What happens if you separate out each operation (eg make an intermediate variable for the namedtuple)? What is triggering the import and/or calling that code?

Best, of course, would be if you could produce a cut-down clonable example that illustrates the problem, so we can see for ourselves.

2 Likes

I’ll work on it perhaps tomorrow and provide just some essential code to reproduce the exception (if I don’t narrow it down before then). Either way, I’ll report back.

Hello again Friends:

I’ve been meaning to return to this open issue. I’m afraid I ran into the same error, but in a different context.

Here’s how to reproduce that different context (distinct from the one above), and where SuspensionError occurs.

In a new App, delete everything and create the following Client Code structure:

my_package                  # A Python package.
  - MyForm                  # An Anvil Form.
  - runtime_event_handlers  # A Python module.

And in each, paste the following:

# ===========================
# In: my_package
# ===========================
# An empty file.
# ===========================
# In: MyForm
# ===========================
from ._anvil_designer import MyFormTemplate
import numbers # This is fine here. But not later on.

class MyForm(MyFormTemplate):
    # =====================================================
    from ..runtime_event_handlers import an_event_handler
    # =====================================================
    def __init__(self, **properties):
        self.init_components(**properties)

and:

# ===========================
# In: runtime_event_handlers
# ===========================
from collections import namedtuple # This is fine.
import numbers   # <---- Emits SuspensionError.

def an_event_handler(self, **event_args): # Never reached.
    pass

Set MyForm as the Startup Form and launch it. You should arrive at a SuspensionError.

I have workarounds to avoid using import numbers, but I feel this should work.

Any ideas? Thank you in advance!

Ah yes the body of a class can’t suspend. This will be fixed in a future update of skulpt.

2 Likes

Thank you!

My alternative (backup implementation) also laments Skulpt (but for a different reason - LoL):

        import locale
        locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

        [ ... snip ... ]

        try:
            self._numeric_answer = float(locale.atof(self._numeric_answer))
        except Exception as e:
            return False, f'Validation Exception:\n\n  >> {e}'
        return True,str()

Emitted in the browser at Run-Time:

NotImplementedImportError: locale is not yet implemented in Skulpt

I only need locale so float() doesn’t complain about commas when trying to convert. But this is for a strictly U.S. use-case, so I can cheat (with a TODO note) and just remove the commas. :slight_smile:

Is there a page listing what Python packages / modules are supported in Skulpt? This way I know not to try something. I did a quick search, but nothing immediately pops up.

EDIT: @stucork Per your below suggestion, I submitted the request here.

Might be best to ask the other questions in new threads as we’re moving off topic here.
The list of available skulpt packages would be a good feature request.

1 Like