initialise debian/
[debian/make-magic.git] / core / bits.py
1 #! /usr/bin/env python
2
3 from uuid import uuid4
4
5 class Scheme(object):
6         def __init__(self, items):
7                 self.items = items
8
9 class BaseItem(object):
10         description = ""
11         depends = ()
12         predicate = lambda task: True
13         name = property(lambda self: self.__class__.__name__)
14         def __repr__(self):
15                 return '<'+self.name+'('+self.__class__.__bases__[0].__name__+') instance>'
16
17 class Item(BaseItem):
18         INCOMPLETE = 'INCOMPLETE'
19         FAILED = 'FAILED'
20         IN_PROGRESS = 'IN_PROGRESS'
21         CANNOT_AUTOMATE = 'CANNOT_AUTOMATE'
22         COMPLETE = 'COMPLETE'
23
24         allowed_states = set((INCOMPLETE,FAILED,IN_PROGRESS,CANNOT_AUTOMATE,COMPLETE))
25         def __init__(self, data=None):
26                 if data == None:
27                         data = dict(state=self.INCOMPLETE)
28                 self.data = data
29         def isComplete(self): 
30                 return self.data['state'] == self.COMPLETE
31
32 class TaskComplete(Item):
33         '''sentinal task that contains all items required for completion'''
34         def __init__(self, goals=None, data=None):
35                 if goals != None:
36                         self.depends = tuple(goals)
37                 if len(self.depends) == 0:
38                         raise ValueError('MUST provide goals to create TaskComplete')
39                 Item.__init__(self,data)
40
41 class DataItem(Item):
42         def __init__(self):
43                 Item.__init__(self)
44                 self.data = None
45
46 class Group(BaseItem):
47         '''"logical" groups containing items
48         groups are sets of items that cannot be started until the group's dependencies are satisfied.
49
50         This sounds perfectly sane until you realise that what you're actually
51         doing is defining every item in the group to be dependent on every dependency of the
52         group.  As groups can contain groups, every item in the whole graph is actually
53         dependent on all dependencies of any groups that contain it, any groups that contain that
54         group, and so on recursively.  This is actually a digraph not a chain so a naive implementation
55         to see which groups contain an item is not going to work.
56
57         Groups make intuitive sense to humans, and make things so much more hackish to implement.
58         They do make it easier for humans to build the original set of dependencies, at the expense
59         of using somewhat of a shotgun approach in dependencies at times.  It can also be used to
60         effectively partition the dependency digraph through critical nodes.
61
62         One solution to applying dependencies of groups to those it contains is to:
63                 - For every group, find the group contents plus the contents of any groups
64                   contained by the group recursively
65                 - For each item, add to it's dependencies the union of the set of dependencies of all
66                   groups it is contained in.
67                   
68         Once that is complete, it is then (if needed) possible to perform a reduction to remove the
69         groups. A simple implementation would be to for each group, find each dependency on that group
70         and replace it with the contents of the group.
71
72         Groups are currently not serialised and stored as state; They should be removed as quickly
73         as possible after task creation
74         '''
75         contains = NotImplemented
76
77 class Task(object):
78         def __init__(self, items, requirements, goal, uuid=None, data=None):
79                 if uuid == None:
80                         uuid = str(uuid4())
81                 if data == None:
82                         data = dict()
83                 self.uuid, self.items, self.requirements, self.data = uuid, items, requirements, data
84                 assert isinstance(goal, TaskComplete)
85                 self.goal = goal
86         def __repr__(self):
87                 return "Task(uuid='%s')" % (self.uuid,)