Beginning Python - From Novice to Professional
Beginning Python - From Novice to Professional Beginning Python - From Novice to Professional
CHAPTER 27 ■ PROJECT 8: FILE SHARING WITH XML-RPC 503 another Node to it. Let’s call these methods query, fetch, and hello. The following is a sketch of the class, written as pseudocode: class Node: def __init__(self, url, dirname, secret): self.url = url self.dirname = dirname self.secret = secret self.known = set() def query(self, query): Look for a file (possibly asking neighbors), and return it as a string def fetch(self, query, secret): If the secret is correct, perform a regular query and store the file. In other words, make the Node find the file and download it. def hello(self, other): Add the other Node to the known peers Assuming that the set of known URLs is called known, the hello method is very simple—it simply adds other to self.known, where other is the only parameter (a URL). However, XML-RPC requires all methods to return a value; None is not accepted. So, let’s define two result “codes” that indicate success or failure: OK = 1 FAIL = 2 Then the hello method can be implemented as follows: def hello(self, other): self.known.add(other) return OK When the Node is registered with a SimpleXMLRPCServer, it will be possible to call this method from the “outside.” The query and fetch methods are a bit more tricky. Let’s begin with fetch because it’s the simpler of the two. It must take two parameters—the query and the “secret,” which is required so that your Node can’t be arbitrarily manipulated by anyone. (Note that calling fetch causes the Node to download a file. Access to this method should therefore be more restricted than, for example, query, which simply passes the file through.) If the supplied secret is not equal to self.secret (the one supplied at startup), fetch simply returns FAIL. Otherwise, it calls query to get the file corresponding to the given query (a file name). But what does query return? When you call query, you would like to know whether or
504 CHAPTER 27 ■ PROJECT 8: FILE SHARING WITH XML-RPC not the query succeeded, and you would like to have the contents of the relevant file returned if it did. So, let’s define the return value of query as the pair (tuple) code, data where code is either OK or FAIL, and data is the sought-after file (if code equals OK) stored in a string, or an arbitrary value (for example, an empty string) otherwise. In fetch, the code and the data are retrieved. If the code is FAIL, then fetch simply returns FAIL as well. Otherwise, it opens a new file (in write mode) whose name is the same as the query, and which is found in self.dirname (you use os.path.join to join the two). The data is written to the file, the file is closed, and OK is returned. See Listing 27-1 later in this section for the relatively straightforward implementation. Now, turn your attention to query. It receives a query as a parameter, but it should also accept a history (which contains URLs that should not be queried because they are already waiting for a response to the same query). Because this history is empty in the first call to query, you can use an empty list as a default value. If you take a look at the code in Listing 27-1, you’ll see that I’ve abstracted away part of the behavior of query by creating two utility methods called _handle and _broadcast. Note that their names begin with underscores—that means that they won’t be accessible through XML- RPC. (This is part of the behavior of SimpleXMLRPCServer, not a part of XML-RPC itself.) That is useful because these methods aren’t meant to provide separate functionality to an outside party, but are there to structure the code. For now, let’s just assume that _handle takes care of the internal handling of a query (checks whether the file exists at this specific Node, fetches the data, and so forth) and that it returns a code and some data, just like query itself is supposed to. As you can see from the listing, if code == OK, then code, data is returned straight away—the file was found. However, what should query do if the code returned from _handle is FAIL? Then it has to ask all other known Nodes for help. The first step in this process is to add self.url to history. ■Note Neither the += operator nor the append list method has been used when updating the history because both of these modify lists in place, and you don’t want to modify the default value itself. If the new history is too long, query returns FAIL (along with an empty string). The max length is arbitrarily set to 6 and kept in the global “constant” MAX_HISTORY_LENGTH. WHY IS MAX_HISTORY_LENGTH SET TO 6? The idea is that any peer in the network should be able to reach another in, at most, six steps. This, of course, depends on the structure of the network (which peers know which), but is supported by the hypothesis of “six degrees of separation,” which applies to people and who they know. For a description of this hypothesis, see the Small World Research Project (http://smallworld.columbia.edu). Using this number in your program may not be very scientific, but at least it seems like a good guess. On the other hand, in a large network with many nodes, the sequential nature of your program may lead to bad performance for large values of MAX_HISTORY_LENGTH, so you might want to reduce it if things get slow.
- Page 483 and 484: 452 CHAPTER 23 ■ PROJECT 4: IN TH
- Page 486 and 487: CHAPTER 24 ■ ■ ■ Project 5: A
- Page 488 and 489: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 490 and 491: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 492 and 493: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 494 and 495: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 496 and 497: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 498 and 499: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 500 and 501: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 502 and 503: CHAPTER 24 ■ PROJECT 5: A VIRTUAL
- Page 504 and 505: CHAPTER 25 ■ ■ ■ Project 6: R
- Page 506 and 507: CHAPTER 25 ■ PROJECT 6: REMOTE ED
- Page 508 and 509: CHAPTER 25 ■ PROJECT 6: REMOTE ED
- Page 510 and 511: CHAPTER 25 ■ PROJECT 6: REMOTE ED
- Page 512: CHAPTER 25 ■ PROJECT 6: REMOTE ED
- Page 515 and 516: 484 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 517 and 518: 486 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 519 and 520: 488 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 521 and 522: 490 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 523 and 524: 492 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 525 and 526: 494 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 527 and 528: 496 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 529 and 530: 498 CHAPTER 26 ■ PROJECT 7: YOUR
- Page 531 and 532: 500 CHAPTER 27 ■ PROJECT 8: FILE
- Page 533: 502 CHAPTER 27 ■ PROJECT 8: FILE
- Page 537 and 538: 506 CHAPTER 27 ■ PROJECT 8: FILE
- Page 539 and 540: 508 CHAPTER 27 ■ PROJECT 8: FILE
- Page 541 and 542: 510 CHAPTER 27 ■ PROJECT 8: FILE
- Page 543 and 544: 512 CHAPTER 27 ■ PROJECT 8: FILE
- Page 545 and 546: 514 CHAPTER 27 ■ PROJECT 8: FILE
- Page 547 and 548: 516 CHAPTER 27 ■ PROJECT 8: FILE
- Page 549 and 550: 518 CHAPTER 28 ■ PROJECT 9: FILE
- Page 551 and 552: 520 CHAPTER 28 ■ PROJECT 9: FILE
- Page 553 and 554: 522 CHAPTER 28 ■ PROJECT 9: FILE
- Page 555 and 556: 524 CHAPTER 28 ■ PROJECT 9: FILE
- Page 558 and 559: CHAPTER 29 ■ ■ ■ Project 10:
- Page 560 and 561: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 562 and 563: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 564 and 565: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 566 and 567: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 568 and 569: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 570 and 571: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 572 and 573: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 574 and 575: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 576 and 577: CHAPTER 29 ■ PROJECT 10: DO-IT-YO
- Page 578 and 579: APPENDIX A ■ ■ ■ The Short Ve
- Page 580 and 581: APPENDIX A ■ THE SHORT VERSION 54
- Page 582 and 583: APPENDIX A ■ THE SHORT VERSION 55
CHAPTER 27 ■ PROJECT 8: FILE SHARING WITH XML-RPC 503<br />
another Node <strong>to</strong> it. Let’s call these methods query, fetch, and hello. The following is a sketch of<br />
the class, written as pseudocode:<br />
class Node:<br />
def __init__(self, url, dirname, secret):<br />
self.url = url<br />
self.dirname = dirname<br />
self.secret = secret<br />
self.known = set()<br />
def query(self, query):<br />
Look for a file (possibly asking neighbors), and return it as<br />
a string<br />
def fetch(self, query, secret):<br />
If the secret is correct, perform a regular query and s<strong>to</strong>re<br />
the file. In other words, make the Node find the file and download it.<br />
def hello(self, other):<br />
Add the other Node <strong>to</strong> the known peers<br />
Assuming that the set of known URLs is called known, the hello method is very simple—it<br />
simply adds other <strong>to</strong> self.known, where other is the only parameter (a URL). However, XML-RPC<br />
requires all methods <strong>to</strong> return a value; None is not accepted. So, let’s define two result “codes”<br />
that indicate success or failure:<br />
OK = 1<br />
FAIL = 2<br />
Then the hello method can be implemented as follows:<br />
def hello(self, other):<br />
self.known.add(other)<br />
return OK<br />
When the Node is registered with a SimpleXMLRPCServer, it will be possible <strong>to</strong> call this method<br />
from the “outside.”<br />
The query and fetch methods are a bit more tricky. Let’s begin with fetch because it’s the<br />
simpler of the two. It must take two parameters—the query and the “secret,” which is required<br />
so that your Node can’t be arbitrarily manipulated by anyone. (Note that calling fetch causes<br />
the Node <strong>to</strong> download a file. Access <strong>to</strong> this method should therefore be more restricted than, for<br />
example, query, which simply passes the file through.)<br />
If the supplied secret is not equal <strong>to</strong> self.secret (the one supplied at startup), fetch simply<br />
returns FAIL. Otherwise, it calls query <strong>to</strong> get the file corresponding <strong>to</strong> the given query (a file<br />
name). But what does query return? When you call query, you would like <strong>to</strong> know whether or