Pi Pico W: print in server functions called by Pico don't show in console or logs

I playing a bit with the Pico W & Anvil. Nice idea.

I now have the Pico call an anvil server function. The latter prints something (for debugging), but it won’t show up in the console, nor in the logs.
In fact, only in the micropython REPL is the print output shown (I use the Mu editor).

It would be helpful to have the print output on the server as well, in case of deployment of Picos. In that case one might not be able to attach to USB and have a REPL into the Pico.

It might also help if there was some (automatically generated) unique identifier for each Pico device connecting to Anvil. This would help distinguish the output generated on different Picos.

1 Like

That’s one of the beauties of Python: you can automate that yourself, to your own specifications, and not have to rely on someone else’s idea of “unique” or “identifier” or “identifier lifetime”.

We wanted to do this too, but unfortunately this one isn’t feasible. One of the limitations of MicroPython is that print() isn’t hookable - it just goes directly to the serial console.

I’ve seen people write list = [ 1, 2, 3 ] in sample code. Which, of course, redefines list. It’s a bad practice, but for cases like this, maybe not so bad.

Alternatively, does a workable function have to be named print?

@meredydd Please note that the print statement is located in the server function . The server function was called by the Pico code, though.
The output from the print statement is shown on in the Pico Repl prefixed with "server: ".

Which suggests (to ignorant me!) that there is an @anvil.server.callable function by that name in your Pico code.

I don’t know what you mean.

The code is very simple:
On the server:

:
@anvil.server.callable
def on_connect(msg=''):
  print(f"on_connect {msg}")

@anvil.server.callable
def on_every_connect(msg=''):
  print(f"on_every_connect {msg}")

On the Pico:

:
async def on_connect(seconds):
    await anvil.pico.call("on_connect", '')

async def on_every_connect(msg):
    await anvil.pico.call("on_every_connect", msg)

anvil.pico.connect(UPLINK_KEY, on_connect(5), on_every_connect=on_every_connect(BOARD_NAME))

As you can see, no string “server:” in sight. And the print statements run on the server not on the Pico.

I do this with lots of old code or when I’m starting a not very important project, it is really quite simple as long as you don’t mind your debugger yelling at you.

What I do (in this order in the script) is make a deepcopy of print, (print2 or whatever), then build a wrapper function that does logging code, then the copy of print.

Then you replace the builtin print with your wrapper function.

from copy import deepcopy

print2 = deepcopy(print)

def print_wrapper(*args, **kwargs):
    #do some logging code
    #do some anvil logging code
    example_send_to_anvil_function(*args, **kwargs)
    
    print2(*args, **kwargs)


print = print_wrapper

This is definitely on the “please don’t do this” list of things people do anyway.

My Uplink code also has @anvil.server.callable functions.

If, by accident, I had also named one the same as a server-side @anvil.server.callable function, then, on call, Anvil would prefer the local version of the function. See below.

See Anvil Docs | Sharing Uplinks

When your app makes an anvil.server.call(), it will look for server functions in these locations, in the following order:

  1. Uplink connected to this environment
  2. Shared Uplinks in other environments of this app
  3. Shared Uplinks in dependency apps
  4. Server Modules

So it might be worth checking to see whether your Uplink code has (accidentally) also defined these same functions.