I’m trying to wrap ttn-lw-cli in python to do batch operations on my applications and devices.
Doing this works and prints the help message
if __name__ == '__main__':
os.system("ttn-lw-cli end-device")
However when I run a command that talks to the actual network, the command hangs and never returns. Ex:
if __name__ == '__main__':
os.system("ttn-lw-cli end-device list")
At this point I need to do a force kill (SIGKILL/9) of the process.
I have also tried using subprocess.run("ttn-lw-cli end-device list") and it has the same behaviour. Also with shell=True and the working directory set to my home dir.
I’ve also specified the location of the config file for ttn-lw-cli with no effect.
Does anyone know why this command would never return when called from python?
The following works for me with Python 3.10.1 in windows, however has issues in running in a Jupyter Notebook due to an issue in Ipykernerl 6.3 - 6.9. https://github.com/ipython/ipykernel/issues/847. So due to this issue there is a rough workaround in this method of a CLI class ( I just show the call function)
def call(self, command, timeout=10):
self._process = subprocess.Popen(
["ttn-lw-cli"] + command,
shell=False,
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
# creationflags=DETACHED_PROCESS
)
logger.debug(f"self._process: {self._process}")
try:
self.stdout, self.stderr = self._process.communicate(timeout=3)
logger.debug(self.stdout.decode("utf-8"))
logger.debug(self.stderr.decode("utf-8"))
return self
except Exception as e:
logger.info(f"Exception: {e}")
logger.info(
f"see: Verbose subprocess and c-library hangs with IPyKernel 6.x.x #847\n"
f"https://github.com/ipython/ipykernel/issues/847 \n"
f"https://www.thethingsnetwork.org/forum/t/calling-ttn-lw-cli-from-python-hangs-forever/54773"
f"Workaround: use powershell to run a script saving stderr and stdout to files\n"
)
# Empty outfile
with open("./data/cli_output.txt", "wb") as f:
f.write(b"")
with open("./data/cli_error.txt", "wb") as f:
f.write(b"")
with open("./data/cli_command.ps1", "w") as f:
workaround_command = (
f"{' '.join(['ttn-lw-cli']+command)} "
f' 2>"./data/cli_error.txt" '
f"| out-file -encoding ASCII ./data/cli_output.txt"
)
logger.debug(f" issuing workaround as:{workaround_command}")
f.write(f"{workaround_command}\n")
self._process = subprocess.Popen(
["powershell.exe", "./data/cli_command.ps1"],
shell=True,
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
creationflags=DETACHED_PROCESS,
)
self.stdout, self.stderr = self._process.communicate(timeout=timeout)
with open("./data/cli_output.txt", "rb") as f:
self.stdout = f.read()
with open("./data/cli_error.txt", "rb") as f:
self.stderr = f.read()
logger.debug(self.stdout.decode("utf-8"))
logger.debug(self.stderr.decode("utf-16"))
if self.stderr != b"":
raise Exception(
f"Exception from ttn_tw_cli:\n" f"{self.stderr.decode('utf-16')}"
)
return self
return None
I’m also having some trouble with the CLI using. I want to use events and store some of the events in a database. I first tried using the Event API, but could not get it to work. Have some of you managed to get it to work with the ttn-lw-cli in python ?