Merge lp:~stefanor/ibid/exchange-825217 into lp:ibid

Proposed by Stefano Rivera
Status: Merged
Approved by: Stefano Rivera
Approved revision: 1068
Merged at revision: 1033
Proposed branch: lp:~stefanor/ibid/exchange-825217
Merge into: lp:ibid
Diff against target: 549 lines (+258/-99)
8 files modified
docs/api/ibid.utils.rst (+8/-1)
ibid/plugins/conversions.py (+140/-68)
ibid/test/__init__.py (+26/-8)
ibid/test/plugins/test_conversions.py (+52/-4)
ibid/test/plugins/test_core.py (+6/-11)
ibid/test/plugins/test_url.py (+3/-3)
ibid/test/test_utils.py (+19/-0)
ibid/utils/__init__.py (+4/-4)
To merge this branch: bzr merge lp:~stefanor/ibid/exchange-825217
Reviewer Review Type Date Requested Status
Max Rabkin Approve
Review via email: mp+71367@code.launchpad.net

Commit message

XE.com no longer has a nice helpful country to currency list, so we build our own, based on ISO-4127. This means many heuristics, so we include a reasonable test suite.

To post a comment you must log in.
lp:~stefanor/ibid/exchange-825217 updated
1033. By Stefano Rivera

Load country_codes in _load_currencies

1034. By Stefano Rivera

Comment data-structures, remove XE.com bracketed place name search heuristics, and return to pre r586 (exchange-343775) heuristics

1035. By Stefano Rivera

Plugin name in cacheable_download location

1036. By Stefano Rivera

Add test suite for resolve_currency (and rename to a public name)

1037. By Stefano Rivera

Filter non-exchangeable currencies

Revision history for this message
Max Rabkin (max-rabkin) wrote :

Some non-currencies are missing (search http://en.wikipedia.org/wiki/ISO_4217 for "funds code" for some more -- the funds codes are in table A.2 of the standard, we may be able to use this instead of hard coding).

You should use trial's tempdir functions instead of hardcoding /tmp.

The tests are networking tesst but it don't check for IBID_NETWORKLESS_TEST.

review: Needs Fixing
lp:~stefanor/ibid/exchange-825217 updated
1038. By Stefano Rivera

Add ibid.test.TestCase, less full-stack than PluginTestCase

1039. By Stefano Rivera

use ibid.test.TestCase for conversions tests

1040. By Stefano Rivera

Use ibid.test.TestCase instead of unittest.TestCase for plugin tests

1041. By Stefano Rivera

More fund codes

Revision history for this message
Stefano Rivera (stefanor) wrote :

A.2 is of course a .doc file. I've just used it to hard-code some more fund codes.

The fund codes don't exactly hurt, they just cause confusion (and one of them matched euro before the euro currency :P )

I implemented mkdtemp, but throwing away the cache after every test is a bit silly, and seeing as our test.ini contains "cachedir = cache", I thought I'd just leave it using that.

lp:~stefanor/ibid/exchange-825217 updated
1042. By Stefano Rivera

Missing super()

1043. By Stefano Rivera

Typos

1044. By Stefano Rivera

Add a full-stack CurrencyConversionTest

1045. By Stefano Rivera

Group 'currencies for country' output by country

1046. By Stefano Rivera

Omit 'No universal currency' entries

1047. By Stefano Rivera

Don't assume we have country_currencies for every country_code

1048. By Stefano Rivera

Try to determine the country code through country_codes if the currency does not begin with a country code

1049. By Stefano Rivera

Identify all countries into country_currencies or warn. Detect non_currencies early

1050. By Stefano Rivera

Do exact search before 'in' searches, fill more entries in country_currencies (including Euro contries)

1051. By Stefano Rivera

Strip whitespace from re-ordered names

1052. By Stefano Rivera

Make sure we get countries that primarily use another country's currency in country_currencies

1053. By Stefano Rivera

Snarky comment for same-currency conversions

Revision history for this message
Max Rabkin (max-rabkin) wrote :

<Taejo> tibid: currencies for Saint Helena
<tibid> Taejo: Ascension And Tristan Da Cunha Saint Helena uses Saint Helena Pound (SHP)
<Taejo> tumbleweed: ^^
<Taejo> should be called "Saint Helena, Ascension and Tristan da Cunha"
<Taejo> but your code to invert "foo, the republic of"
<Taejo> also "Taiwan, Province of China"
<Taejo> looking through the list, one solution is to check that it ends with " of" before inverting

review: Needs Fixing
lp:~stefanor/ibid/exchange-825217 updated
1054. By Stefano Rivera

Tweak country comma-reordering to not reorder compound names.

1055. By Stefano Rivera

Document ibid.utils.get_country_codes

1056. By Stefano Rivera

Tweak country comma-reordering to not reorder compound names. in ibid.utils, add tests.

Revision history for this message
Stefano Rivera (stefanor) wrote :

Found a minutely better solution and added a test case. Also some missing documentation and stuff. Sorry the commits are rather unorganised.

lp:~stefanor/ibid/exchange-825217 updated
1057. By Stefano Rivera

etree's iter() function is only in 2.7 (even if it makes lound deprecation noises)

1058. By Stefano Rivera

Ag screw it, let's stop rotating country names with commas in them. Too messy

1059. By Stefano Rivera

Country: Currency name

1060. By Stefano Rivera

More test cases for Currency Lookup

1061. By Stefano Rivera

Don't let currencies that don't start with country-codes fall through the gaps

1062. By Stefano Rivera

s/Sint/Saint/ only works if you do it everywhere

1063. By Stefano Rivera

Drop country names, the currency table we are using disambiguates currency names sufficiently

1064. By Stefano Rivera

Don't pre-compile regexes unecessarily

1065. By Stefano Rivera

Merge nested if pair

Revision history for this message
Max Rabkin (max-rabkin) :
review: Approve
lp:~stefanor/ibid/exchange-825217 updated
1066. By Stefano Rivera

Only do the inexact currency name search if rough

1067. By Stefano Rivera

Add fallthrough test case

1068. By Stefano Rivera

Handle reversed format conversion questions, such as 'convert GBP 1 to ZAR'. Otherwise units will grab them

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'docs/api/ibid.utils.rst'
2--- docs/api/ibid.utils.rst 2011-01-21 21:20:22 +0000
3+++ docs/api/ibid.utils.rst 2011-10-23 20:17:24 +0000
4@@ -125,9 +125,16 @@
5
6 Returns the filename to the resource.
7
8+.. function:: get_country_codes()
9+
10+ Retrieve and decode a list of ISO-3166-1 country codes.
11+
12+ Returns a dict of code -> country_name.
13+ The codes are capitalised.
14+
15 .. function:: identity_name(event, identity)
16
17- Refer to *identity* naturally in response to *event*.
18+ Refer to *identity* naturally in response to *event*.
19
20 URL Functions
21 -------------
22
23=== modified file 'ibid/plugins/conversions.py'
24--- ibid/plugins/conversions.py 2010-09-13 09:22:29 +0000
25+++ ibid/plugins/conversions.py 2011-10-23 20:17:24 +0000
26@@ -9,10 +9,10 @@
27
28 import ibid
29 from ibid.plugins import Processor, handler, match
30-from ibid.compat import any, defaultdict
31+from ibid.compat import any, defaultdict, ElementTree
32 from ibid.config import Option
33-from ibid.utils import file_in_path, get_country_codes, human_join, \
34- unicode_output, generic_webservice
35+from ibid.utils import (cacheable_download, file_in_path, get_country_codes,
36+ human_join, unicode_output, generic_webservice)
37 from ibid.utils.html import get_html_parse_tree
38
39 features = {}
40@@ -312,86 +312,153 @@
41 country_codes = {}
42
43 def _load_currencies(self):
44- etree = get_html_parse_tree(
45- 'http://www.xe.com/iso4217.php', headers = {
46- 'User-Agent': 'Mozilla/5.0',
47- 'Referer': 'http://www.xe.com/',
48- }, treetype='etree')
49-
50- tbl_main = [x for x in etree.getiterator('table') if x.get('class') == 'tbl_main'][0]
51-
52+ iso4127_file = cacheable_download(
53+ 'http://www.currency-iso.org/dl_iso_table_a1.xml',
54+ 'conversions/iso4217.xml')
55+ document = ElementTree.parse(iso4127_file)
56+ # Code -> [Countries..., Currency Name]
57 self.currencies = {}
58- for tbl_sub in tbl_main.getiterator('table'):
59- if tbl_sub.get('class') == 'tbl_sub':
60- for tr in tbl_sub.getiterator('tr'):
61- code, place = [x.text for x in tr.getchildren()]
62- name = u''
63- if not place:
64- place = u''
65- if u',' in place[1:-1]:
66- place, name = place.split(u',', 1)
67- place = place.strip()
68- if code in self.currencies:
69- currency = self.currencies[code]
70- # Are we using another country's currency?
71- if place != u'' and name != u'' and (currency[1] == u'' or currency[1].rsplit(None, 1)[0] in place
72- or (u'(also called' in currency[1] and currency[1].split(u'(', 1)[0].rsplit(None, 1)[0] in place)):
73- currency[0].insert(0, place)
74- currency[1] = name.strip()
75- else:
76- currency[0].append(place)
77- else:
78- self.currencies[code] = [[place], name.strip()]
79+ # Country -> Code
80+ self.country_currencies = {}
81+ self.country_codes = get_country_codes()
82+ # Non-currencies:
83+ non_currencies = set(('BOV CLF COU MXV '
84+ 'UYI XSU XUA ' # Various Fund codes
85+ 'CHE CHW ' # Swiss WIR currencies
86+ 'USN USS ' # US Dollar fund codes
87+ 'XAG XAU XPD XPT ' # Metals
88+ 'XBA XBB XBC XBD ' # Euro Bond Market
89+ 'XDR XTS XXX ' # Other specials
90+ ).split())
91+ no_country_codes = set(('Saint Martin',
92+ 'Virgin Islands (Us)',
93+ 'Virgin Islands (British)',))
94+ accociated_all_countries = True
95+ for currency in document.getiterator('ISO_CURRENCY'):
96+ code = currency.findtext('ALPHABETIC_CODE').strip()
97+ name = currency.findtext('CURRENCY').strip()
98+ place = currency.findtext('ENTITY').strip().title()
99+ if code == '' or code in non_currencies:
100+ continue
101+ # Fund codes
102+ if re.match(r'^Zz[0-9]{2}', place, re.UNICODE):
103+ continue
104+ if code in self.currencies:
105+ self.currencies[code][0].append(place)
106+ else:
107+ self.currencies[code] = [[place], name]
108+ if place in no_country_codes:
109+ continue
110+ if (code[:2] in self.country_codes
111+ and code[:2] not in self.country_currencies):
112+ self.country_currencies[code[:2]] = code
113+ continue
114+ ascii_place = (unicodedata.normalize('NFD', unicode(place))
115+ .encode('ASCII', 'ignore')
116+ .replace('-', ' ')
117+ .replace('Sint', 'Saint'))
118+
119+ # Countries with (alternative names)
120+ swapped_place = None
121+ m = re.match(r'^(.+?)\s+\((.+)\)$', ascii_place)
122+ if m is not None:
123+ swapped_place = '%s (%s)' % (m.group(2), m.group(1))
124+
125+ for ccode, country in self.country_codes.iteritems():
126+ country = country.title()
127+ ascii_country = (unicodedata.normalize('NFD', country)
128+ .encode('ASCII', 'ignore')
129+ .replace('-', ' ')
130+ .replace('Sint', 'Saint'))
131+ if ascii_country in (ascii_place, swapped_place):
132+ if ccode not in self.country_currencies:
133+ self.country_currencies[ccode] = code
134+ break
135+ else:
136+ log.info(u"ISO4127 parsing: Can't identify %s as a known "
137+ u"country", place)
138+ accociated_all_countries = False
139
140 # Special cases for shared currencies:
141- self.currencies['EUR'][0].insert(0, u'Euro Member Countries')
142- self.currencies['XOF'][0].insert(0, u'Communaut\xe9 Financi\xe8re Africaine')
143- self.currencies['XOF'][1] = u'Francs'
144+ self.currencies['EUR'][0].append(u'Euro Member Countries')
145+ self.currencies['XAF'][0].append(u"Communaut\xe9 financi\xe8re d'Afrique")
146+ self.currencies['XCD'][0].append(u'Organisation of Eastern Caribbean States')
147+ self.currencies['XOF'][0].append(u'Coop\xe9ration financi\xe8re en Afrique centrale')
148+ self.currencies['XPF'][0].append(u'Comptoirs Fran\xe7ais du Pacifique')
149+ return accociated_all_countries
150
151- def _resolve_currency(self, name, rough=True):
152+ def resolve_currency(self, name, rough=True, plural_recursion=False):
153 "Return the canonical name for a currency"
154
155+ if not self.currencies:
156+ self._load_currencies()
157+
158 if name.upper() in self.currencies:
159 return name.upper()
160
161- strip_currency_re = re.compile(r'^[\.\s]*([\w\s]+?)s?$', re.UNICODE)
162- m = strip_currency_re.match(name)
163-
164+ # Strip leading dots (.TLD)
165+ m = re.match(r'^[\.\s]*(.+)$', name, re.UNICODE)
166 if m is None:
167 return False
168-
169 name = m.group(1).lower()
170
171- # TLD -> country name
172- if rough and len(name) == 2 and name.upper() in self.country_codes:
173- name = self.country_codes[name.upper()].lower()
174+ # TLD:
175+ if rough and len(name) == 2 and name.upper() in self.country_currencies:
176+ return self.country_currencies[name.upper()]
177
178 # Currency Name
179 if name == u'dollar':
180 return "USD"
181-
182- name_re = re.compile(r'^(.+\s+)?\(?%ss?\)?(\s+.+)?$' % name, re.I | re.UNICODE)
183+ if name == u'pound':
184+ return "GBP"
185 for code, (places, currency) in self.currencies.iteritems():
186- if name_re.match(currency) or [True for place in places if name_re.match(place)]:
187- return code
188-
189+ if name == currency.lower():
190+ return code
191+ if name.title() in places:
192+ return code
193+
194+ # There are also country names in country_codes:
195+ for code, place in self.country_codes.iteritems():
196+ if name == place.lower() and code in self.country_currencies:
197+ return self.country_currencies[code]
198+
199+ # Second pass, not requiring exact match:
200+ if rough:
201+ for code, (places, currency) in self.currencies.iteritems():
202+ if name in currency.lower():
203+ return code
204+ if any(name in place.lower() for place in places):
205+ return code
206+
207+ for code, place in self.country_codes.iteritems():
208+ if name in place.lower() and code in self.country_currencies:
209+ return self.country_currencies[code]
210+
211+ # Maybe it's a plural?
212+ if name.endswith('s') and not plural_recursion:
213+ return self.resolve_currency(name[:-1], rough, True)
214 return False
215
216 @match(r'^(exchange|convert)\s+([0-9.]+)\s+(.+)\s+(?:for|to|into)\s+(.+)$')
217 def exchange(self, event, command, amount, frm, to):
218- if not self.currencies:
219- self._load_currencies()
220-
221- if not self.country_codes:
222- self.country_codes = get_country_codes()
223-
224 rough = command.lower() == 'exchange'
225
226- canonical_frm = self._resolve_currency(frm, rough)
227- canonical_to = self._resolve_currency(to, rough)
228+ canonical_frm = self.resolve_currency(frm, rough)
229+ canonical_to = self.resolve_currency(to, rough)
230 if not canonical_frm or not canonical_to:
231 if rough:
232- event.addresponse(u"Sorry, I don't know about a currency for %s", (not canonical_frm and frm or to))
233+ event.addresponse(
234+ u"Sorry, I don't know about a currency for %s",
235+ (not canonical_frm and frm or to))
236+ return
237+ if canonical_frm == canonical_to:
238+ event.addresponse(
239+ u"Um, that's the same currency. Tell you what, "
240+ u"I can offer you my special rate of 0.5 %(currency)s for "
241+ u"each %(code)s you sell me.", {
242+ 'currency': self.currencies[canonical_frm][1],
243+ 'code': canonical_frm,
244+ })
245 return
246
247 data = generic_webservice(
248@@ -407,14 +474,12 @@
249 return
250
251 event.addresponse(
252- u'%(fresult)s %(fcode)s (%(fcountry)s %(fcurrency)s) = '
253- u'%(tresult)0.2f %(tcode)s (%(tcountry)s %(tcurrency)s) '
254+ u'%(fresult)s %(fcode)s (%(fcurrency)s) = '
255+ u'%(tresult)0.2f %(tcode)s (%(tcurrency)s) '
256 u'(Last trade rate: %(rate)s, Bid: %(bid)s, Ask: %(ask)s)', {
257 'fresult': amount,
258 'tresult': float(amount) * float(last_trade_rate),
259- 'fcountry': self.currencies[canonical_frm][0][0],
260 'fcurrency': self.currencies[canonical_frm][1],
261- 'tcountry': self.currencies[canonical_to][0][0],
262 'tcurrency': self.currencies[canonical_to][1],
263 'fcode': canonical_frm,
264 'tcode': canonical_to,
265@@ -423,21 +488,28 @@
266 'ask': ask,
267 })
268
269+ @match(r'^(exchange|convert)\s+(.+)\s+([0-9.]+)\s+(?:for|to|into)\s+(.+)$')
270+ def exchange_reversed(self, event, command, amount, frm, to):
271+ self.exchange(event, command, frm, amount, to)
272+
273+
274 @match(r'^(?:currency|currencies)\s+for\s+(?:the\s+)?(.+)$')
275 def currency(self, event, place):
276 if not self.currencies:
277 self._load_currencies()
278
279- search = re.compile(place, re.I)
280- results = []
281- for code, (places, name) in self.currencies.iteritems():
282- for place in places:
283- if search.search(place):
284- results.append(u'%s uses %s (%s)' % (place, name, code))
285+ results = defaultdict(list)
286+ for code, (c_places, name) in self.currencies.iteritems():
287+ for c_place in c_places:
288+ if re.search(place, c_place, re.I):
289+ results[c_place].append(u'%s (%s)' % (name, code))
290 break
291
292 if results:
293- event.addresponse(human_join(results))
294+ event.addresponse(human_join(
295+ u'%s uses %s' % (place, human_join(currencies))
296+ for place, currencies in results.iteritems()
297+ ))
298 else:
299 event.addresponse(u'No currencies found')
300
301
302=== modified file 'ibid/test/__init__.py'
303--- ibid/test/__init__.py 2011-06-20 20:59:56 +0000
304+++ ibid/test/__init__.py 2011-10-23 20:17:24 +0000
305@@ -6,7 +6,7 @@
306 import os
307 from traceback import format_exception
308 import re
309-from shutil import copyfile
310+import shutil
311 import sys
312 import tempfile
313
314@@ -72,21 +72,37 @@
315 return None
316
317
318-class PluginTestCase(unittest.TestCase):
319+class TestCase(unittest.TestCase):
320+ """TestCase subclass, implementing:
321+ * detection for tests using network resources
322+ * basic Ibid configuration
323+ """
324+ network = False
325+
326+ def setUp(self):
327+ super(TestCase, self).setUp()
328+ if self.network and os.getenv('IBID_NETWORKLESS_TEST') is not None:
329+ raise unittest.SkipTest('test uses network')
330+ ibid.config = FileConfig(locate_resource('ibid.test', 'test.ini'))
331+
332+
333+class PluginTestCase(TestCase):
334+ """A full-stack plugin test, implementing:
335+ * Loading of the specified plugins before running the tests, and cleanup
336+ afterwards
337+ * DB support (clean DB for each TestCase)
338+ * Test events passed through the standard Ibid event dispatcher
339+ """
340 load = []
341 noload = []
342 load_base = True
343 load_configured = None
344 username = u'user'
345 public = False
346- network = False
347 empty_dbfile = None
348
349 def setUp(self):
350- if self.network and os.getenv('IBID_NETWORKLESS_TEST') is not None:
351- raise unittest.SkipTest('test uses network')
352-
353- ibid.config = FileConfig(locate_resource('ibid.test', 'test.ini'))
354+ super(PluginTestCase, self).setUp()
355
356 if self.load_configured is None:
357 self.load_configured = not self.load
358@@ -145,7 +161,7 @@
359 if self.empty_dbfile is None:
360 self._create_empty_database()
361 self.dbfile = self.mktemp()
362- copyfile(self.empty_dbfile, self.dbfile)
363+ shutil.copyfile(self.empty_dbfile, self.dbfile)
364 ibid.config['databases']['ibid'] = 'sqlite:///' + self.dbfile
365
366 def make_event(self, message=None, type=u'message'):
367@@ -216,6 +232,8 @@
368 self.fail("Event was expected to fail", event)
369
370 def tearDown(self):
371+ super(PluginTestCase, self).tearDown()
372+
373 for processor in ibid.processors:
374 processor.shutdown()
375 del ibid.processors[:]
376
377=== modified file 'ibid/test/plugins/test_conversions.py'
378--- ibid/test/plugins/test_conversions.py 2011-01-26 12:20:59 +0000
379+++ ibid/test/plugins/test_conversions.py 2011-10-23 20:17:24 +0000
380@@ -1,9 +1,11 @@
381-# Copyright (c) 2010-2011, Max Rabkin
382+# Copyright (c) 2010-2011, Max Rabkin, Stefano Rivera
383 # Released under terms of the MIT/X/Expat Licence. See COPYING for details.
384
385-from ibid.test import PluginTestCase
386-
387-class UnihanTest(PluginTestCase):
388+import logging
389+
390+import ibid.test
391+
392+class UnihanTest(ibid.test.PluginTestCase):
393 load = ['conversions']
394 network = True
395
396@@ -12,3 +14,49 @@
397 u'.*the traditional form is \u99AC')
398 self.assertResponseMatches(u'unihan \u99AC',
399 u'.*the simplified form is \u9A6C')
400+
401+class CurrencyLookupTest(ibid.test.TestCase):
402+ network = True
403+
404+ def setUp(self):
405+ super(CurrencyLookupTest, self).setUp()
406+ from ibid.plugins import conversions
407+ self.processor = conversions.Currency(u'testplugin')
408+
409+ def test_country_association(self):
410+ self.assertTrue(self.processor._load_currencies())
411+
412+ def test_common_currencies(self):
413+ self.assertEqual(self.processor.resolve_currency('pound', True), 'GBP')
414+ self.assertEqual(self.processor.resolve_currency('dollar', True), 'USD')
415+ self.assertEqual(self.processor.resolve_currency('euro', True), 'EUR')
416+ self.assertEqual(self.processor.resolve_currency('rand', True), 'ZAR')
417+
418+ def test_tld(self):
419+ self.assertEqual(self.processor.resolve_currency('.za', True), 'ZAR')
420+ self.assertEqual(self.processor.resolve_currency('.na', True), 'NAD')
421+ self.assertEqual(self.processor.resolve_currency('.ch', True), 'CHF')
422+ # Currency from a former country
423+ self.assertEqual(self.processor.resolve_currency('.sx', True), 'ANG')
424+ # X- Currency
425+ self.assertEqual(self.processor.resolve_currency('.cm', True), 'XAF')
426+
427+ def test_country(self):
428+ self.assertEqual(self.processor.resolve_currency('united kingdom', True), 'GBP')
429+ self.assertEqual(self.processor.resolve_currency('south africa', True), 'ZAR')
430+ self.assertEqual(self.processor.resolve_currency('bosnia', True), 'BAM')
431+
432+ def test_fallthrough(self):
433+ self.assertFalse(self.processor.resolve_currency('oz', False))
434+
435+class CurrencyConversionTest(ibid.test.PluginTestCase):
436+ load = ['conversions']
437+ network = True
438+
439+ def test_conversion(self):
440+ self.assertResponseMatches(u'exchange 1 Pound for ZAR',
441+ r'1 GBP \(.+\) = [0-9.]+ ZAR \(.+\) .* Bid: [0-9.]+')
442+ self.assertResponseMatches(u'exchange 1 France for Egypt',
443+ r'1 EUR \(.+\) = [0-9.]+ EGP \(.+\) .* Bid: [0-9.]+')
444+ self.assertResponseMatches(u'exchange 1 Virgin Islands for .tv',
445+ r'1 USD \(.+\) = [0-9.]+ AUD \(.+\) .* Bid: [0-9.]+')
446
447=== modified file 'ibid/test/plugins/test_core.py'
448--- ibid/test/plugins/test_core.py 2010-05-01 10:02:44 +0000
449+++ ibid/test/plugins/test_core.py 2011-10-23 20:17:24 +0000
450@@ -1,22 +1,17 @@
451 # Copyright (c) 2009-2010, Jeremy Thurgood and Max Rabkin
452 # Released under terms of the MIT/X/Expat Licence. See COPYING for details.
453
454-from twisted.trial import unittest
455-
456+import ibid
457 import ibid.test
458 from ibid.event import Event
459
460-class TestAddressed(unittest.TestCase):
461+class TestAddressed(ibid.test.TestCase):
462
463 def setUp(self):
464- ibid.test.set_config({
465- u'botname': u'test_ibid',
466- u'plugins': {
467- u'testplugin': {
468- u'names': [u'test_ibid', u'bot', u'ant']
469- },
470- },
471- })
472+ super(TestAddressed, self).setUp()
473+ ibid.config.botname = u'test_ibid'
474+ ibid.config.plugins['testplugin'] = ibid.test.FakeConfig(
475+ {'names': [u'test_ibid', u'bot', u'ant']})
476
477 from ibid.plugins import core
478 self.processor = core.Addressed(u'testplugin')
479
480=== modified file 'ibid/test/plugins/test_url.py'
481--- ibid/test/plugins/test_url.py 2010-01-18 22:45:15 +0000
482+++ ibid/test/plugins/test_url.py 2011-10-23 20:17:24 +0000
483@@ -1,13 +1,13 @@
484 # Copyright (c) 2009-2010, Stefano Rivera
485 # Released under terms of the MIT/X/Expat Licence. See COPYING for details.
486
487-from twisted.trial import unittest
488-
489+import ibid.test
490 from ibid.plugins import urlgrab
491
492-class TestURLGrabber(unittest.TestCase):
493+class TestURLGrabber(ibid.test.TestCase):
494
495 def setUp(self):
496+ super(TestURLGrabber, self).setUp()
497 self.grab = urlgrab.Grab(u'testplugin')
498
499 good_grabs = [
500
501=== added file 'ibid/test/test_utils.py'
502--- ibid/test/test_utils.py 1970-01-01 00:00:00 +0000
503+++ ibid/test/test_utils.py 2011-10-23 20:17:24 +0000
504@@ -0,0 +1,19 @@
505+# Copyright (c) 2011, Stefano Rivera
506+# Released under terms of the MIT/X/Expat Licence. See COPYING for details.
507+
508+import datetime
509+
510+import ibid.test
511+import ibid.utils
512+
513+class TestUtils(ibid.test.TestCase):
514+ def test_ago(self):
515+ self.assertEqual(ibid.utils.ago(datetime.timedelta(seconds=60)), u'1 minute')
516+ self.assertEqual(ibid.utils.ago(datetime.timedelta(seconds=60000), 1), u'16 hours')
517+
518+class TestUtilsNetwork(ibid.test.TestCase):
519+ network = True
520+
521+ def test_get_country_codes(self):
522+ codes = ibid.utils.get_country_codes()
523+ self.assertIn('ZA', codes)
524
525=== modified file 'ibid/utils/__init__.py'
526--- ibid/utils/__init__.py 2011-01-22 23:04:00 +0000
527+++ ibid/utils/__init__.py 2011-10-23 20:17:24 +0000
528@@ -376,9 +376,9 @@
529 line = line.strip()
530 if started and ';' in line:
531 country, code = line.split(u';')
532- if u',' in country:
533- country = u' '.join(reversed(country.split(u',', 1)))
534- country = country.title()
535+ country = country.lower()
536+ # Hack around http://bugs.python.org/issue7008
537+ country = country.title().replace(u"'S", u"'s")
538 countries[code] = country
539 elif line == u'':
540 started = True
541@@ -387,7 +387,7 @@
542
543 return countries
544
545-def identity_name (event, identity):
546+def identity_name(event, identity):
547 if event.identity == identity.id:
548 return u'you'
549 elif event.source == identity.source:

Subscribers

People subscribed via source and target branches

to all changes: