Source code for blume.farm

"""Split the farm from magic.

The reason for this split is a growing belief that almost everything in the farm,
and the Farm itself should be some sort of Ball.

And wanting the default Farm to have a basic structure running with
everything a typical Ball might need, it needs to import some examples
from other modules.  Since those modules use magic, the end result is
a circular import.

This splt also allows me to think about what lives where.  And how
everything relates.  

Plan
====

This Farm is currently just a directed graph.  It feels like round
abouts should be the graph nodes, with their edges being the graph
edges.

That should simplify things a little from the current code.

I would like to change how the graph connects objects dynamically, in
a similar way to the way I can change attributes of running Ball's
with the magic.Interact object.

The Farm seems the right place -- or maybe the Shepherd?

Objects with a start and a run.

That might start and run each other.

Balls can write things to a queue.

The plan in magic land is to get the magic roundabout working.

At the moment there is a magic roundabout for each Ball.

"""
import sys

import inspect

import random

import math

import io

from pathlib import Path

from collections import deque, defaultdict

import datetime

#import curio
import asyncio
curio = asyncio

import numpy as np

from PIL import Image

import matplotlib

from matplotlib import figure, rc

from matplotlib import pyplot as plt

from blume import magic, console
from .magic import Ball, RoundAbout, Shepherd, Carpet, spawn

from .mclock2 import GuidoClock
from .rcparms import Params
from .console import Console


[docs] class Farm(Ball): """ A farm, for now.. A network of things running around. Magic round-abouts of queues connecting it all. Displays, keyboards, human input, outputs. A graph of tools. And something to help it run. """ def __init__(self, hub=None, nodes=None, edges=None): """ Turn graph into a running farm """ super().__init__() self.pause = True hub = hub or DiGraph() hub.add_nodes_from(nodes or set()) hub.add_edges_from(edges or set()) print('OK to here') self.hub = hub self.shep = Shepherd() # register quit event with shepherd self.shep.add_filter('q', self.quit) print('OK to here2') # start a farm going carpet = Carpet() self.carpet = carpet clock = GuidoClock() self.shell = console.Console( farm=self, carpet=carpet, shepherd=self.shep, tmra=magic.TheMagicRoundAbout, ) #background = sys.platform != 'emscriptem' background = True self.add_node(self.shell, background=background) self.add_node(carpet, background=True) self.add_node(self.shep) self.add(Params()) self.add(clock) # connections #self.add_edge(carpet, hat) # sheperd looking after gfarm.hub, which it is itself part of. self.shep.set_flock(self.hub) # initial path this needs more thought - let's do it in start() self.shep.set_path([self.shep, self.carpet]) def __getattr__(self, attr): """ Delegate to hub self.hub is a directed graph, so we're a Ball that is a graph """ #print(f'farm looking for {attr}') if attr == 'hub': raise AttributeError return getattr(self.hub, attr) def status(self): print(f' nodes: {self.hub.number_of_nodes()}') print(f' edges: {self.hub.number_of_edges()}') hub = self.hub for item in hub.nodes: print(hub[item]) for edge in hub.edges: print(edge, hub.edges[edge]) def add(self, item): #self.add_edge(item, self.carpet) self.add_edge(self.carpet, item)
[docs] async def start(self): """ Traverse the graph do some plumbing? Let the shepherd look after the running of everything """ # Tell the shepherd what to look after self.shep.flock = self.hub print('GEEEFAR starting shep') result = self.shep.start() if inspect.iscoroutine(result): await result # create a task which is a dog watching the shepherd print('GEEEFAR spawning superdog') self.superdog = spawn(magic.canine(self.shep)) # set the shepherd to pause self.shep.pause = True # figure out an initial path await self.shep.show_help()
[docs] async def run(self): """ Run the farm For now, just plot the current graph. """ print('MAGIC TREE FARM') # wait for the super dog try: await self.superdog except asyncio.CancelledError: print('Farm shutting down')
[docs] async def quit(self): """ quit the farm """ await self.shep.quit() self.superdog.cancel()
class DiGraph: def __init__(self): self.nodes = defaultdict(dict) self.edges = defaultdict(dict) def add_nodes_from(self, nodes): for node in nodes: self.add_node(node) def add_edges_from(self, edges): for edge in edges: self.add_edge(*edge) def add_node(self, node, **keyw): self.nodes[node].update(**keyw) def add_edge(self, a, b, **keyw): self.edges[(a, b)].update(**keyw) for node in a, b: if node not in self.nodes: self.nodes[node].update(**keyw) def succcessors(self, node): result = [] for a, b in self.edges.keys(): if a is node: result.append(b) def predecessors(self, node): result = [] for a, b in self.edges.keys(): if b is node: result.append(a) def __iter__(self): return iter(self.nodes) def __len__(self): return len(self.nodes) # example below ignore for now
[docs] class MagicPlot(Ball): """ Use a Ball to plot. FIXME: make this one more interesting. """ def __init__(self): super().__init__() self.add_filter('a', self.add)
[docs] async def add(self): """ Magic Plot key demo """ print('magic key was pressed') # now what to add? return math.pi + math.e
async def start(self): print('magic plot started') self.fig = figure.Figure() self.ax = self.fig.add_subplot(111) async def run(self): ax = await self.get() ax.clear() data = np.random.randint(50, size=100) ax.plot(data) ax.show()
def run(farm=None, balls=None, dump=False): if farm is None: farm = Farm() if balls is None: balls = MagicPlot() for ball in balls: farm.add(ball) print('set up the farm .. move to start for added thrills? or not?') #farm.setup() if dump: print() print('DUMP') farm.dump() magic.run(start_and_run(farm)) async def start_and_run(farm): print('starting farm') await farm.start() print('running farm') runner = await farm.run() if __name__ == '__main__': run()