summaryrefslogtreecommitdiff
path: root/src/zope/security/examples/sandbox.py
blob: b10d7b6e2b6efe5f3c65d69492372d96e131df40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A small sandbox application.
"""
import random
import time

from zope.interface import Interface
from zope.interface import implementer


class IAgent(Interface):
    """A player/agent in the world.

    The agent represents an autonomous unit, that lives in various
    homes/sandboxes and accesses services present at the sandboxes. Agents are
    imbued with a sense of wanderlust and attempt to find new homes after a
    few turns of the time generator (think turn based games).
    """
    def action():
        """Perform agent's action."""

    def setHome(home):
        """Move to a different home."""

    def getHome():
        """Return the place where the agent currently lives."""

    def getAuthenticationToken():
        """Return the authority by which the agent perform actions."""


class IService(Interface):
    """Marker to designate some form of functionality.

    Services are available from sandboxes, examples include time service,
    agent discovery, and sandbox discovery.
    """


class ISandbox(Interface):
    """A container for agents to live in and services to be available."""

    def getService(service_id):
        """Get the service having the provided id in this sandbox."""

    def getAgents():
        """Return a list of agents living in this sandbox."""

    def addAgent(agent):
        """Add a new agent to the sandbox."""

    def transportAgent(agent, destination):
        """Move the specified agent to the destination sandbox."""


class SandboxError(Exception):
    """A sandbox error is thrown, if any action could not be performed."""
    pass


class Identity:
    """Mixin for pretty printing and identity method"""

    def __init__(self, id, *args, **kw):
        self.id = id

    def getId(self):
        return self.id

    def __str__(self):
        return "<{}> {}".format(str(self.__class__.__name__), str(self.id))

    __repr__ = __str__


@implementer(IAgent)
class Agent(Identity):

    def __init__(self, id, home, auth_token, action):
        """Initialize agent."""
        self.id = id
        self.auth_token = auth_token
        self.home = home
        self._action = action

    def action(self):
        """See IAgent."""
        self._action(self, self.getHome())

    def setHome(self, home):
        """See IAgent."""
        self.home = home

    def getHome(self):
        """See IAgent."""
        return self.home

    def getAuthenticationToken(self):
        """See IAgent."""
        return self.auth_token


@implementer(ISandbox)
class Sandbox(Identity):
    """
    see ISandbox doc
    """

    def __init__(self, id, service_factories):
        self.id = id
        self._services = {}
        self._agents = {}

        for sf in service_factories:
            self.addService(sf())

    def getAgentIds(self):
        return self._agents.keys()

    def getAgents(self):
        return self._agents.values()

    def getServiceIds(self):
        return self._services.keys()

    def getService(self, sid):
        return self._services.get(sid)

    def getHome(self):
        return self

    def addAgent(self, agent):
        if agent.getId() not in self._agents \
                and IAgent.providedBy(agent):
            self._agents[agent.getId()] = agent
            agent.setHome(self)
        else:
            raise SandboxError("couldn't add agent %s" % agent)

    def addService(self, service):

        if not service.getId() in self._services \
                and IService.providedBy(service):
            self._services[service.getId()] = service
            service.setHome(self)
        else:
            raise SandboxError("couldn't add service %s" % service)

    def transportAgent(self, agent, destination):
        if agent.getId() in self._agents \
                and destination is not self \
                and ISandbox.providedBy(destination):
            destination.addAgent(agent)
            del self._agents[agent.getId()]
        else:
            raise SandboxError("couldn't transport agent {} to {}".format(
                agent, destination)
            )


@implementer(IService)
class Service:
    def getId(self):
        return self.__class__.__name__

    def setHome(self, home):
        self._home = home

    def getHome(self):
        return getattr(self, '_home')


class HomeDiscoveryService(Service):
    """
    returns the ids of available agent homes
    """

    def getAvailableHomes(self):
        return _homes.keys()


class AgentDiscoveryService(Service):
    """
    returns the agents available at a given home
    """

    def getLocalAgents(self, home):
        return home.getAgents()


class TimeService(Service):
    """
    returns the local time
    """

    def getTime(self):
        return time.time()


default_service_factories = (
    HomeDiscoveryService,
    AgentDiscoveryService,
    TimeService
)


def action_find_homes(agent, home):
    home_service = home.getService('HomeDiscoveryService')
    return home_service.getAvailableHomes()


def action_find_neighbors(agent, home):
    agent_service = home.getService('AgentDiscoveryService')
    return agent_service.getLocalAgents(home)


def action_find_time(agent, home):
    time_service = home.getService('TimeService')
    return time_service.getTime()


class TimeGenerator:
    """Represents the passage of time in the agent simulation.

    each turn represents some discrete unit of time, during
    which all agents attempt to perform their action. Additionally,
    all agents are checked to see if they have a desire to move,
    and if so are transported to a new random home.
    """

    def setupAgent(self, agent):
        pass

    def teardownAgent(self, agent):
        pass

    def turn(self):

        global _homes

        for h in _homes.values():
            agents = h.getAgents()
            for a in agents:
                self.setupAgent(a)
                try:
                    a.action()
                except Exception as e:
                    print('-- Exception --')
                    print('"%s" in "%s" not allow to "%s"'
                          % (a, h, a._action.__name__))
                    print(e)
                    print()
                self.teardownAgent(a)

            agents = filter(WanderLust, agents)

            for a in agents:
                self.setupAgent(a)
                try:
                    home = a.getHome()
                    new_home = GreenerPastures(a)
                    home.transportAgent(a, new_home)
                except Exception as e:
                    print('-- Exception --')
                    print('moving "%s" from "%s" to "%s"' %
                          (a, h, repr(new_home)))
                    print(e)
                    print()
                self.teardownAgent(a)


def WanderLust(agent):
    """ is agent ready to move """
    if int(random.random() * 100) <= 30:
        return 1


def GreenerPastures(agent):
    """ where do they want to go today """
    global _homes
    possible_homes = _homes.keys()
    possible_homes.remove(agent.getHome().getId())
    return _homes.get(random.choice(possible_homes))


# boot strap initial setup.

# global list of homes
_homes = {}

all_homes = (
    Sandbox('jail', default_service_factories),
    Sandbox('origin', default_service_factories),
    Sandbox('valhalla', default_service_factories)
)

origin = all_homes[1]

for h in all_homes:
    _homes[h.getId()] = h


agents = [
    Agent('odin', None, 'norse legend', action_find_time),
    Agent('loki', None, 'norse legend', action_find_neighbors),
    Agent('thor', None, 'norse legend', action_find_homes),
    Agent('thucydides', None, 'greek men', action_find_time),
    Agent('archimedes', None, 'greek men', action_find_neighbors),
    Agent('prometheus', None, 'greek men', action_find_homes),
]

for a in agents:
    origin.addAgent(a)


def main():
    world = TimeGenerator()

    for x in range(5):
        print('world turning')
        world.turn()

    for h in _homes.values():
        print(h.getId(), h.getAgentIds())


if __name__ == '__main__':
    main()