commit 0e4f8bfbdcf1bc9e427748dd37b53ad6b4a32d93 Author: Damian Johnson atagar@torproject.org Date: Sun Sep 9 15:13:32 2012 -0700
Parsing the known-flags attribute
Only changes are...
* Moving the Flag enum to stem.descriptor.Flag since it's something that users will commonly use. As a rule of thumb I'd like users to only need 'stem.descriptor' unless they're doing something fancy. However, this is just a temporary move - I plan to move Flag to is own module, like Version.
* Excluding empty strings from our known_flags attribute (our prior behavior was to parse things like " " into ['', '', '', '']). The spec doesn't set any constraints on what a flag can be so I suppose technically one could be the empty string, though this'll never be the case. Still waffling back and forth about if this should cause a validation error or not... --- stem/descriptor/__init__.py | 17 +++++++++++++++ stem/descriptor/networkstatus.py | 27 +++++------------------ test/integ/descriptor/networkstatus.py | 3 +- test/unit/descriptor/networkstatus/document.py | 24 ++++++++++++++++++++- test/unit/descriptor/networkstatus/entry.py | 3 +- 5 files changed, 50 insertions(+), 24 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py index 688e1b6..b921058 100644 --- a/stem/descriptor/__init__.py +++ b/stem/descriptor/__init__.py @@ -27,12 +27,29 @@ import re import datetime import collections
+import stem.util.enum + KEYWORD_CHAR = "a-zA-Z0-9-" WHITESPACE = " \t" KEYWORD_LINE = re.compile("^([%s]+)(?:[%s]+(.*))?$" % (KEYWORD_CHAR, WHITESPACE)) PGP_BLOCK_START = re.compile("^-----BEGIN ([%s%s]+)-----$" % (KEYWORD_CHAR, WHITESPACE)) PGP_BLOCK_END = "-----END %s-----"
+Flag = stem.util.enum.Enum( + ("AUTHORITY", "Authority"), + ("BADEXIT", "BadExit"), + ("EXIT", "Exit"), + ("FAST", "Fast"), + ("GUARD", "Guard"), + ("HSDIR", "HSDir"), + ("NAMED", "Named"), + ("RUNNING", "Running"), + ("STABLE", "Stable"), + ("UNNAMED", "Unnamed"), + ("V2DIR", "V2Dir"), + ("VALID", "Valid"), +) + def parse_file(path, descriptor_file): """ Provides an iterator for the descriptors within a given file. diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index eca3b71..beafe89 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -52,7 +52,6 @@ except: import stem.descriptor import stem.version import stem.exit_policy -import stem.util.enum import stem.util.tor_tools
from stem.descriptor import _read_until_keywords, _peek_keyword, _strptime @@ -61,21 +60,6 @@ from stem.descriptor import _read_keyword_line, _read_keyword_line_str, _get_pse _bandwidth_weights_regex = re.compile(" ".join(["W%s=\d+" % weight for weight in ["bd", "be", "bg", "bm", "db", "eb", "ed", "ee", "eg", "em", "gb", "gd", "gg", "gm", "mb", "md", "me", "mg", "mm"]]))
-Flag = stem.util.enum.Enum( - ("AUTHORITY", "Authority"), - ("BADEXIT", "BadExit"), - ("EXIT", "Exit"), - ("FAST", "Fast"), - ("GUARD", "Guard"), - ("HSDIR", "HSDir"), - ("NAMED", "Named"), - ("RUNNING", "Running"), - ("STABLE", "Stable"), - ("UNNAMED", "Unnamed"), - ("V2DIR", "V2Dir"), - ("VALID", "Valid"), -) - # Network status document are either a 'vote' or 'consensus', with different # mandatory fields for each. Both though require that their fields appear in a # specific order. This is an ordered listing of the following... @@ -383,6 +367,11 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): except ValueError: if validate: raise ValueError("Network status document's '%s' line had '%s', which isn't a parseable tor version: %s" % (keyword, entry, line)) + elif keyword == "known-flags": + # "known-flags" FlagList + + # simply fetches the entries, excluding empty strings + self.known_flags = [entry for entry in value.split(" ") if entry]
# doing this validation afterward so we know our 'is_consensus' and # 'is_vote' attributes @@ -408,14 +397,10 @@ class NetworkStatusDocument(stem.descriptor.Descriptor): _read_keyword_line("voting-delay", content, False, True) _read_keyword_line("client-versions", content, False, True) _read_keyword_line("server-versions", content, False, True) + _read_keyword_line("known-flags", content, False, True)
vote = self.is_vote
- flags_content = _read_keyword_line("known-flags", content, validate) - - if flags_content: - self.known_flags = flags_content.split(" ") - read_keyword_line("params", True) if self.params: self.params = dict([(param.split("=")[0], int(param.split("=")[1])) for param in self.params.split(" ")]) diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py index 9087b22..d77a36f 100644 --- a/test/integ/descriptor/networkstatus.py +++ b/test/integ/descriptor/networkstatus.py @@ -11,6 +11,7 @@ import unittest
import stem.exit_policy import stem.version +import stem.descriptor import stem.descriptor.networkstatus import test.integ.descriptor
@@ -47,7 +48,7 @@ class TestNetworkStatus(unittest.TestCase):
# check if there's any unknown flags for flag in desc.flags: - if not flag in stem.descriptor.networkstatus.Flag: + if not flag in stem.descriptor.Flag: # TODO: this should be a special 'new capability' check later # rather than failing the tests raise ValueError("Unrecognized flag type: %s, found on relay %s (%s)" % (flag, desc.fingerprint, desc.nickname)) diff --git a/test/unit/descriptor/networkstatus/document.py b/test/unit/descriptor/networkstatus/document.py index 032c095..5e3ec4b 100644 --- a/test/unit/descriptor/networkstatus/document.py +++ b/test/unit/descriptor/networkstatus/document.py @@ -6,7 +6,8 @@ import datetime import unittest
import stem.version -from stem.descriptor.networkstatus import HEADER_STATUS_DOCUMENT_FIELDS, FOOTER_STATUS_DOCUMENT_FIELDS, Flag, NetworkStatusDocument, DirectorySignature +from stem.descriptor import Flag +from stem.descriptor.networkstatus import HEADER_STATUS_DOCUMENT_FIELDS, FOOTER_STATUS_DOCUMENT_FIELDS, NetworkStatusDocument, DirectorySignature
NETWORK_STATUS_DOCUMENT_ATTR = { "network-status-version": "3", @@ -354,4 +355,25 @@ class TestNetworkStatusDocument(unittest.TestCase):
document = NetworkStatusDocument(content, False) self.assertEquals(expected_value, getattr(document, attr)) + + def test_known_flags(self): + """ + Parses some known-flag entries. Just exercising the field, there's not much + to test here. + """ + + test_values = ( + ("", []), + (" ", []), + ("BadExit", [Flag.BADEXIT]), + ("BadExit ", [Flag.BADEXIT]), + ("BadExit ", [Flag.BADEXIT]), + ("BadExit Fast", [Flag.BADEXIT, Flag.FAST]), + ("BadExit Unrecognized Fast", [Flag.BADEXIT, "Unrecognized", Flag.FAST]), + ) + + for test_value, expected_value in test_values: + content = get_network_status_document({"known-flags": test_value}) + document = NetworkStatusDocument(content) + self.assertEquals(expected_value, document.known_flags)
diff --git a/test/unit/descriptor/networkstatus/entry.py b/test/unit/descriptor/networkstatus/entry.py index 195600c..7c9e051 100644 --- a/test/unit/descriptor/networkstatus/entry.py +++ b/test/unit/descriptor/networkstatus/entry.py @@ -5,7 +5,8 @@ Unit tests for the RouterStatusEntry of stem.descriptor.networkstatus. import datetime import unittest
-from stem.descriptor.networkstatus import Flag, RouterStatusEntry, _decode_fingerprint +from stem.descriptor import Flag +from stem.descriptor.networkstatus import RouterStatusEntry, _decode_fingerprint from stem.version import Version from stem.exit_policy import MicrodescriptorExitPolicy
tor-commits@lists.torproject.org