Dear lord. I finally, after MANY headaches, managed to get pathfinding working for my mindless little critters in LibTCodPy (after following the Complete Roguelike Tutorial to its end and then being left to my own devices).

Ran into all sorts of annoying problems caused by the structure of the code previously - for example, I only wanted my creatures to have Paths, not every object in the game, so I thought the best way to do this would be to create the path (see
here) when I was instantiating the creatures in the place_objects function, as per the tutorial.
Of course, this didn't work, because I was trying to create the paths using my fov_map as the pathfinding map...and the fov_map is created after make_map is called. And it's make_map that calls place_objects. My creatures were being created before the fov_map, but needing fov_map to be given paths. Sigh.
I guess I could've split out place_objects from make_map and done that after creating the fov_map - reshuffled the code a bit, etc - but instead I decided that since I only wanted living, breathing,
moving creatures to have paths, I'd build it into the BasicMonsterAI composition class instead.
Python then threw a tantrum about me wanting to do this in the init function of the class (gave me a very odd error about trying to pass a path as a pointer, as far as I could tell), so instead built it into the take_turn function, so that the first time a creature's AI tries to take a turn it'll check to see if the creature itself has a path object, create one if not, and then proceed to use it as applicable.
And, after a couple of minor syntax hiccups,
it lives!

My creatures now joyously path to the player and try to kill them, if they can see you. There's not much visibly different from the previous simple method (which simply had the creature move towards you if it was in your FOV), but this means that (a) if you move out of sight they can now go to the last position they saw you in and (b) I can now start to build on this to get some proper, sensible movement in place!
It's a tiny, tiny achievement that's entirely unimpressive to anybody but myself, but I'm so psyched right now that I'm grinning like a maniac.

Basic AI code snippet below in case anybody wants to poke through and see it. Most of the code from the Complete Roguelike Tutorial available
here, with slight modifications.

class BasicMonsterAI:
#AI for a basic monster
def take_turn(self):
#a basic monster takes its turn. If you can see it, it can see you.
monster = self.owner
#first, check its path item exists
if monster.path == None:
monster.path = libtcod.path_new_using_map(fov_map,1.41)
if libtcod.map_is_in_fov(fov_map, monster.x, monster.y):
#move towards the player if far away
if monster.distance_to(player) >= 2:
#compute how to reach the player
libtcod.path_compute(monster.path,monster.x, monster.y, player.x, player.y)
#and move one square towards them
nextx,nexty=libtcod.path_walk(monster.path,True)
monster.move_towards(nextx,nexty)
#now we're close enough to attack! ...assuming the player is alive
elif player.fighter.hp > 0:
monster.fighter.attack(player)
else:
#the player cannot see the monster
#if we have an old path, continue to follow it -
# this means that monsters you dodge will go to where they last saw you!
if not libtcod.path_is_empty(monster.path):
nextx,nexty=libtcod.path_walk(monster.path,True)
monster.move_towards(nextx,nexty)
else:
#our path is empty, just wander randomly
randdirection = libtcod.random_get_int(0,1,8)
if randdirection == 1:
monster.move(-1,1)
elif randdirection == 2:
monster.move(0,1)
elif randdirection == 3:
monster.move(1,1)
elif randdirection == 4:
monster.move(-1,0)
elif randdirection == 5:
monster.move(1,0)
elif randdirection == 6:
monster.move(-1,-1)
elif randdirection == 7:
monster.move(0,-1)
else:
monster.move(1,-1)