bspl 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. bspl-1.0.0/LICENSE +21 -0
  2. bspl-1.0.0/MANIFEST.in +5 -0
  3. bspl-1.0.0/PKG-INFO +372 -0
  4. bspl-1.0.0/README.org +328 -0
  5. bspl-1.0.0/pyproject.toml +6 -0
  6. bspl-1.0.0/setup.cfg +75 -0
  7. bspl-1.0.0/setup.py +25 -0
  8. bspl-1.0.0/src/bspl/__init__.py +2 -0
  9. bspl-1.0.0/src/bspl/adapter/__init__.py +3 -0
  10. bspl-1.0.0/src/bspl/adapter/core.py +624 -0
  11. bspl-1.0.0/src/bspl/adapter/emitter.py +253 -0
  12. bspl-1.0.0/src/bspl/adapter/event.py +28 -0
  13. bspl-1.0.0/src/bspl/adapter/jason.py +190 -0
  14. bspl-1.0.0/src/bspl/adapter/message.py +248 -0
  15. bspl-1.0.0/src/bspl/adapter/policies.py +264 -0
  16. bspl-1.0.0/src/bspl/adapter/policy.gr +32 -0
  17. bspl-1.0.0/src/bspl/adapter/receiver.py +134 -0
  18. bspl-1.0.0/src/bspl/adapter/scheduler.py +94 -0
  19. bspl-1.0.0/src/bspl/adapter/schema.py +41 -0
  20. bspl-1.0.0/src/bspl/adapter/statistics.py +79 -0
  21. bspl-1.0.0/src/bspl/adapter/store.py +316 -0
  22. bspl-1.0.0/src/bspl/generators/__init__.py +12 -0
  23. bspl-1.0.0/src/bspl/generators/asl.py +273 -0
  24. bspl-1.0.0/src/bspl/generators/mambo.py +75 -0
  25. bspl-1.0.0/src/bspl/generators/node_red.py +268 -0
  26. bspl-1.0.0/src/bspl/langshaw.py +664 -0
  27. bspl-1.0.0/src/bspl/main.py +132 -0
  28. bspl-1.0.0/src/bspl/parsers/bspl/__init__.py +128 -0
  29. bspl-1.0.0/src/bspl/parsers/bspl/bspl.gr +32 -0
  30. bspl-1.0.0/src/bspl/parsers/bspl/bspl_parser.py +322 -0
  31. bspl-1.0.0/src/bspl/parsers/bspl/build.py +24 -0
  32. bspl-1.0.0/src/bspl/parsers/langshaw/__init__.py +39 -0
  33. bspl-1.0.0/src/bspl/parsers/langshaw/build.py +24 -0
  34. bspl-1.0.0/src/bspl/parsers/langshaw/langshaw.gr +46 -0
  35. bspl-1.0.0/src/bspl/parsers/precedence/__init__.py +18 -0
  36. bspl-1.0.0/src/bspl/parsers/precedence/precedence.gr +33 -0
  37. bspl-1.0.0/src/bspl/protocol.py +733 -0
  38. bspl-1.0.0/src/bspl/utils.py +45 -0
  39. bspl-1.0.0/src/bspl/validation.py +107 -0
  40. bspl-1.0.0/src/bspl/verification/__init__.py +195 -0
  41. bspl-1.0.0/src/bspl/verification/logic.py +98 -0
  42. bspl-1.0.0/src/bspl/verification/lpaths.py +569 -0
  43. bspl-1.0.0/src/bspl/verification/mambo.py +415 -0
  44. bspl-1.0.0/src/bspl/verification/paths.py +882 -0
  45. bspl-1.0.0/src/bspl/verification/precedence.py +411 -0
  46. bspl-1.0.0/src/bspl/verification/refinement.py +153 -0
  47. bspl-1.0.0/src/bspl/verification/sat.py +485 -0
  48. bspl-1.0.0/src/bspl.egg-info/PKG-INFO +372 -0
  49. bspl-1.0.0/src/bspl.egg-info/SOURCES.txt +71 -0
  50. bspl-1.0.0/src/bspl.egg-info/dependency_links.txt +1 -0
  51. bspl-1.0.0/src/bspl.egg-info/entry_points.txt +2 -0
  52. bspl-1.0.0/src/bspl.egg-info/requires.txt +26 -0
  53. bspl-1.0.0/src/bspl.egg-info/top_level.txt +1 -0
  54. bspl-1.0.0/tests/test_adapter_emissions.py +487 -0
  55. bspl-1.0.0/tests/test_bspl.py +122 -0
  56. bspl-1.0.0/tests/test_core.py +128 -0
  57. bspl-1.0.0/tests/test_emitter.py +53 -0
  58. bspl-1.0.0/tests/test_generators.py +189 -0
  59. bspl-1.0.0/tests/test_langshaw.py +943 -0
  60. bspl-1.0.0/tests/test_mambo.py +367 -0
  61. bspl-1.0.0/tests/test_message.py +101 -0
  62. bspl-1.0.0/tests/test_paths.py +138 -0
  63. bspl-1.0.0/tests/test_performance.py +244 -0
  64. bspl-1.0.0/tests/test_policies.py +145 -0
  65. bspl-1.0.0/tests/test_precedence.py +200 -0
  66. bspl-1.0.0/tests/test_protocol.py +104 -0
  67. bspl-1.0.0/tests/test_refinement.py +224 -0
  68. bspl-1.0.0/tests/test_scheduler.py +26 -0
  69. bspl-1.0.0/tests/test_schema.py +33 -0
  70. bspl-1.0.0/tests/test_statistics.py +19 -0
  71. bspl-1.0.0/tests/test_store.py +138 -0
  72. bspl-1.0.0/tests/test_verification.py +144 -0
bspl-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT/Expat License
2
+
3
+ Copyright (c) 2022 Samuel Christie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
bspl-1.0.0/MANIFEST.in ADDED
@@ -0,0 +1,5 @@
1
+ include *.txt
2
+ include *.org
3
+ recursive-include src *.py
4
+ recursive-include src *.gr
5
+ recursive-include docs *.txt
bspl-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,372 @@
1
+ Metadata-Version: 2.4
2
+ Name: bspl
3
+ Version: 1.0.0
4
+ Summary: Protocol verification tool for BSPL
5
+ Home-page: https://gitlab.com/masr/bspl
6
+ Author: Samuel Christie
7
+ Author-email: shcv@sdf.org
8
+ Project-URL: Bug Reports, https://gitlab.com/masr/bspl/-/issues
9
+ Project-URL: Source, https://gitlab.com/masr/bspl
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/plain
22
+ License-File: LICENSE
23
+ Requires-Dist: TatSu
24
+ Requires-Dist: simplejson
25
+ Requires-Dist: ttictoc
26
+ Requires-Dist: fire
27
+ Requires-Dist: aiocron
28
+ Requires-Dist: croniter
29
+ Requires-Dist: pyyaml
30
+ Requires-Dist: ijson
31
+ Requires-Dist: aiorun
32
+ Requires-Dist: uvloop; sys_platform != "win32"
33
+ Requires-Dist: colorama
34
+ Requires-Dist: agentspeak
35
+ Provides-Extra: testing
36
+ Requires-Dist: pytest; extra == "testing"
37
+ Provides-Extra: sat
38
+ Requires-Dist: boolexpr; extra == "sat"
39
+ Provides-Extra: jason
40
+ Requires-Dist: agentspeak; extra == "jason"
41
+ Provides-Extra: experiments
42
+ Requires-Dist: spearmint; extra == "experiments"
43
+ Dynamic: license-file
44
+
45
+ * BSPL
46
+ BSPL stands for the Blindingly Simple Protocol Language.
47
+
48
+ This repository provides tools for working with the language, including a parser and verification tools (proving safety, liveness, etc.)
49
+ It also provides a library for implementing agents that can enact BSPL protocols.
50
+
51
+ ** Installation
52
+ BSPL may eventually be made available through the PyPI package registry, but for now install it directly from github.
53
+
54
+ *** Prerequisites
55
+ BSPL requires python version 3.8 or later.
56
+ Python 3.9+ is recommended for full compatibility with all features.
57
+
58
+ *** Install from source (Gitlab)
59
+ 1. Download the source code from gitlab:
60
+ #+begin_example
61
+ $ git clone https://gitlab.com/masr/bspl.git
62
+ #+end_example
63
+ 2. Optionally create virtual environment for BSPL
64
+ #+begin_example
65
+ $ python -m venv venv
66
+ #+end_example
67
+ 3. Activate virtual environment (do this whenever you start a new shell session)
68
+ - Linux:
69
+ #+begin_example
70
+ $ . venv/bin/activate
71
+ #+end_example
72
+ - Windows:
73
+ #+begin_example
74
+ $ venv\Scripts\activate.bat # or venv\Scripts\Activate.ps1 in PowerShell
75
+ #+end_example
76
+ If you get an error on Windows because running script is disabled, you can fix it by running the following command:
77
+ #+begin_example PowerShell
78
+ $ Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
79
+ #+end_example
80
+ 3. Install package in development mode
81
+ #+begin_example
82
+ $ pip install -e ./bspl # or . if inside the bspl directory
83
+ #+end_example
84
+
85
+
86
+ ** Usage
87
+ When installed globally, BSPL provides the 'bspl' entrypoint, that can be used as follows:
88
+
89
+ #+begin_example
90
+ SYNOPSIS
91
+ bspl GROUP | COMMAND - (Alternatively, python -m bspl.main on Windows)
92
+
93
+ GROUPS
94
+ GROUP is one of the following:
95
+
96
+ generate
97
+
98
+ verify
99
+
100
+ COMMANDS
101
+ COMMAND is one of the following:
102
+
103
+ ast
104
+ Print the parsed AST for the specification in PATH
105
+
106
+ json
107
+ Print a JSON representation of each protocol
108
+
109
+ check-syntax
110
+ Parse each file, printing any syntax errors found
111
+
112
+ load-file
113
+ Load a BSPL file, returning a Specification object containing one or more protocols
114
+ #+end_example
115
+
116
+ The CLI is implemented using the ~Fire~ library, which means that each command supports the "-h" help option to list the possible arguments.
117
+ Grouped commands, such as those under ~verify~, may inherit arguments from their parent, so check both.
118
+
119
+ ~Fire~ enables inspection and chaining of function results, such as with the ~load-file~ command for loading a specification.
120
+ After the specification is loaded, you can inspect its contents and call methods on them, etc.
121
+
122
+ ~Fire~ can also launch a python shell with various objects available via ~-- --interactive~.
123
+ For example, try ~bspl load-file samples/trade-finance/purchase.bspl protocols Purchase -- --interactive~, and you should get a python REPL with the Purchase protocol loaded in the ~result~ variable.
124
+
125
+ There are some example protocol files in the 'samples' directory.
126
+
127
+ *** Notes
128
+ - There are probably still some bugs. If you find a case that doesn't work as you expect, please submit an issue.
129
+ - For easier testing of listings used in latex papers, BSPL will automatically strip some latex formatting before parsing a protocol, so they should work without modification.
130
+
131
+ ** Implementing Agents
132
+ This repository provides a /protocol adapter/ for implementing agents that play roles in a BSPL protocol.
133
+
134
+ You can run the scenario implementation using the ~start.sh~ script (or ~start.ps1~ on Windows) to launch all of the agents at the same time; press any key to kill them and stop the run.
135
+
136
+ *** Specifying the Protocol
137
+ Our tutorial will follow the Logistics scenario, visible in the scenarios directory.
138
+
139
+ The protocol could be provided as an object in the python code directly, but it is generally easier to specify it as a separate BSPL file and then load it in the agent.
140
+ The protocol we will be using is as follows, given in logistics.bspl:
141
+ #+begin_src bspl
142
+ Logistics {
143
+ roles Merchant, Wrapper, Labeler, Packer
144
+ parameters out orderID key, out itemID key, out item, out status
145
+ private address, label, wrapping
146
+
147
+ Merchant -> Labeler: RequestLabel[out orderID key, out address]
148
+ Merchant -> Wrapper: RequestWrapping[in orderID key, out itemID key, out item]
149
+ Wrapper -> Packer: Wrapped[in orderID key, in itemID key, in item, out wrapping]
150
+ Labeler -> Packer: Labeled[in orderID key, in address, out label]
151
+ Packer -> Merchant: Packed[in orderID key, in itemID key, in wrapping, in label, out status]
152
+ }
153
+ #+end_src
154
+
155
+ This protocol describes roles for four agents, each of which can be implemented either using Python decorators or AgentSpeak (ASL).
156
+
157
+ *** Loading and Configuring the Protocol
158
+ First, load the protocol and export it as a module:
159
+ #+begin_src python
160
+ import bspl
161
+ logistics = bspl.load_file("logistics.bspl").export("Logistics")
162
+ from Logistics import Merchant, Wrapper, Labeler, Packer
163
+ from Logistics import RequestLabel, RequestWrapping, Packed
164
+ #+end_src
165
+
166
+ *** Configuring the Agent
167
+ After loading its protocol, role, and possibly messages, each agent will need to be configured with information about how to connect to the other agents.
168
+
169
+ In the logistics scenario, all of this is done in a common configuration.py file that can be loaded by all four of the agents, but they could be configured separately.
170
+
171
+ #+begin_src python
172
+ agents = {
173
+ "Merchant": [("127.0.0.1", 8000)],
174
+ "Wrapper": [("127.0.0.1", 8001)],
175
+ "Labeler": [("127.0.0.1", 8002)],
176
+ "Packer": [("127.0.0.1", 8003)],
177
+ }
178
+
179
+ systems = {
180
+ "logistics": {
181
+ "protocol": logistics,
182
+ "roles": {
183
+ Merchant: "Merchant",
184
+ Wrapper: "Wrapper",
185
+ Labeler: "Labeler",
186
+ Packer: "Packer",
187
+ },
188
+ },
189
+ }
190
+ #+end_src
191
+ The role binding configuration is a single dictionary mapping roles to tuples containing (IP, port) pairs.
192
+ They don't all have to be on the same machine, but in this example we are running them all on localhost (127.0.0.1)
193
+ The configuration that each agent sees for itself identifies what IP address and port it should listen on; 127.0.0.1 is chosen here so that they listen on all IPs known to the host networking system.
194
+
195
+ *** Implementing Agent Behavior
196
+ There are two approaches to implementing agent behavior in BSPL:
197
+
198
+ **** Approach 1: Using Python Decorators
199
+ This approach is straightforward for request-response patterns and is used in the logistics scenario.
200
+
201
+ ***** Setting up the Agent
202
+ First, create the basic agent structure:
203
+
204
+ #+begin_src python
205
+ import logging
206
+ from bspl.adapter import Adapter
207
+ from configuration import systems, agents
208
+ from Logistics import RequestLabel, RequestWrapping, Packed
209
+
210
+ adapter = Adapter("Merchant", systems, agents)
211
+ logger = logging.getLogger("merchant")
212
+ if __name__ == "__main__":
213
+ logger.info("Starting Merchant...")
214
+ adapter.start()
215
+ #+end_src
216
+
217
+ ***** Acting Proactively
218
+ To start an enactment of a protocol, some agent will have to make the first move. In Logistics, that's the Merchant, who requests the wrapping and labeling of the items in an order. This can be seen from the first message in the protocol, RequestLabel, which has all its parameters labeled ~out~, which means it has no dependencies and can be sent at will.
219
+
220
+ #+begin_src python
221
+ async def order_generator():
222
+ """Generates sample orders."""
223
+ for orderID in range(10):
224
+ await adapter.send(
225
+ RequestLabel(
226
+ orderID=orderID,
227
+ address=random.choice(["Lancaster University", "NCSU"]),
228
+ )
229
+ )
230
+ for i in range(2):
231
+ await adapter.send(
232
+ RequestWrapping(
233
+ orderID=orderID,
234
+ itemID=i,
235
+ item=random.choice(["ball", "bat", "plate", "glass"]),
236
+ )
237
+ )
238
+ await asyncio.sleep(0)
239
+ #+end_src
240
+
241
+ A lot of new things here:
242
+ - The function is asynchronous, to work with the adapter
243
+ - Messages are constructed by passing in their parameters as keyword arguments
244
+ - The resulting message instances are sent with ~adapter.send(message)~
245
+ - There's an ~asyncio.sleep(0)~ step near the end to make sure it doesn't all happen instantly
246
+
247
+ ***** Adding a Reactor
248
+ In most protocols, not all messages are sent independently. Instead, many have dependencies and may follow a simple request/response pattern. We can handle these using a reactor:
249
+
250
+ #+begin_src python
251
+ @adapter.reaction(Packed)
252
+ async def packed(msg):
253
+ """Handle packed items."""
254
+ logger.info(f"Order {msg['orderID']} item {msg['itemID']} packed with status: {msg['status']}")
255
+ return msg
256
+
257
+ if __name__ == "__main__":
258
+ adapter.start(order_generator())
259
+ #+end_src
260
+
261
+ The reactor is registered using the ~@adapter.reaction~ decorator and is called whenever a matching message is received. The message instance is passed as a parameter, containing all the message's data.
262
+
263
+ **** Approach 2: Using AgentSpeak (ASL)
264
+ For more complex behaviors with interdependent states, you can use ASL files. The BSPL compiler can generate ASL templates for your protocol:
265
+
266
+ #+begin_src bash
267
+ python -m bspl.main generate asl logistics.bspl --all_roles
268
+ #+end_src
269
+
270
+ This generates basic ASL files that need to be enhanced with business logic. Here's an example of the generated vs. working code for the Wrapper role:
271
+
272
+ Generated template:
273
+ #+begin_src asl
274
+ +request_wrapping(MasID, Merchant, Wrapper, OrderID, ItemID, Item)
275
+ <- // insert code to compute Wrapped out parameters ['wrapping'] here
276
+ .emit(wrapped(MasID, Wrapper, Packer, OrderID, ItemID, Item, Wrapping)).
277
+ #+end_src
278
+
279
+ Working implementation:
280
+ #+begin_src asl
281
+ +request_wrapping(System, Merchant, Wrapper, OrderID, ItemID, Item)
282
+ <- // Generate wrapping based on item
283
+ if (Item == "ball") {
284
+ Wrapping = "box"
285
+ } else {
286
+ if (Item == "bat") {
287
+ Wrapping = "tube"
288
+ } else {
289
+ if (Item == "plate") {
290
+ Wrapping = "bubble wrap"
291
+ } else {
292
+ Wrapping = "foam"
293
+ }
294
+ }
295
+ };
296
+ .print("Wrapper: Using ", Wrapping, " for item ", Item, " (Order ", OrderID, ")");
297
+ .emit(wrapped(System, Wrapper, "Packer", OrderID, ItemID, Item, Wrapping)).
298
+ #+end_src
299
+
300
+ Key changes needed to make generated ASL files work:
301
+ 1. Replace ~MasID~ with ~System~ for protocol identification
302
+ 2. Add concrete business logic to compute output parameters
303
+ 3. Use string literals for known role names (e.g., ~"Packer"~)
304
+ 4. Add logging for better visibility of the protocol execution
305
+
306
+ ***** Setting up an ASL Agent
307
+ The Python code for an ASL agent is much simpler than the decorator approach:
308
+
309
+ #+begin_src python
310
+ from bspl.adapter import Adapter
311
+ from configuration import systems, agents
312
+
313
+ adapter = Adapter("Wrapper", systems, agents)
314
+ adapter.load_asl("wrapper.asl")
315
+
316
+ if __name__ == "__main__":
317
+ print("Starting Wrapper...")
318
+ adapter.start()
319
+ #+end_src
320
+
321
+ ***** Complex Coordination Example
322
+ The Packer role demonstrates how ASL handles complex coordination between multiple messages:
323
+
324
+ #+begin_src asl
325
+ // Handle wrapped item
326
+ +wrapped(System, Wrapper, Packer, OrderID, ItemID, Item, Wrapping)
327
+ : labeled(System, Labeler, Packer, OrderID, Address, Label)
328
+ <- !send_packed(System, Packer, "Merchant", OrderID, ItemID, Item, Wrapping, Label).
329
+
330
+ // Handle labeled item
331
+ +labeled(System, Labeler, Packer, OrderID, Address, Label)
332
+ : wrapped(System, Wrapper, Packer, OrderID, ItemID, Item, Wrapping)
333
+ <- !send_packed(System, Packer, "Merchant", OrderID, ItemID, Item, Wrapping, Label).
334
+
335
+ // Send packed item
336
+ +!send_packed(System, Packer, Merchant, OrderID, ItemID, Item, Wrapping, Label)
337
+ <- // Generate status based on wrapping and label
338
+ if (Wrapping == "box" & Label == "UK-LANCS-001") {
339
+ Status = "ready for UK shipping"
340
+ } else {
341
+ if (Wrapping == "box" & Label == "US-NCSU-001") {
342
+ Status = "ready for US shipping"
343
+ } else {
344
+ Status = "ready for shipping"
345
+ }
346
+ };
347
+ .print("Packer: Item ", Item, " from order ", OrderID, " is ", Status);
348
+ .emit(packed(System, Packer, Merchant, OrderID, ItemID, Item, Wrapping, Label, Status)).
349
+ #+end_src
350
+
351
+ The Packer waits for both ~wrapped~ and ~labeled~ messages before sending the ~packed~ message, using ASL's context conditions (~:~) to ensure proper synchronization.
352
+
353
+ *** Choosing an Approach
354
+ - Use Python decorators (like in logistics/) when:
355
+ - You have simple request-response patterns
356
+ - You want straightforward, procedural code
357
+ - You need to integrate with Python libraries
358
+
359
+ - Use ASL files (like in grading/) when:
360
+ - You need complex rule-based behavior
361
+ - Your agent has many interdependent states
362
+ - You want a more declarative programming style
363
+
364
+ *** Running the Scenario
365
+ The ~start.sh~ script (or ~start.ps1~ on Windows) launches all agents:
366
+ #+begin_src bash
367
+ ./start.sh
368
+ # On Windows:
369
+ # ./start.ps1
370
+ #+end_src
371
+
372
+ This will start all agents and begin processing orders. Press any key to stop the scenario.