Message ID | d79709a55ccea63af94e61a0c1c00aeedff265ca.1517700836.git.mschiffer@universe-factory.net (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | Kalle Valo |
Headers | show |
On Sun, Feb 04, 2018 at 12:36:54AM +0100, Matthias Schiffer wrote: > When playing with the generation scripts for OpenWrt development, I noticed > that these scripts still required Python 2. Future-proof them by replacing > deprecated functions with new Python 3 compatible variants. The result > works with both Python 2.7 and Python 3.x; older Python 2.x releases are > not supported anymore. > > regulatory.db and regulatory.bin are unchanged and reproducible across > Python versions. Note that there is no stable release of m2crypto for > Python 3 yet; I used the current development branch for testing. I can't say I'm all that knowledgable about Python 2 to Python 3 conversion, but as far as I can tell this looks okay. It does seem to work for me running with both Python 2 and Python 3. One question below though, mostly just to satisfy my curiousity. > Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net> > --- > > v2: explicitly open input file with UTF-8 encoding; otherwise the scripts > will fail without a UTF-8 locale set in the environment > > > db2bin.py | 22 ++++++++--------- > db2fw.py | 28 +++++++++++----------- > dbparse.py | 81 +++++++++++++++++++++++++++++++++++++------------------------- > 3 files changed, 74 insertions(+), 57 deletions(-) > > diff --git a/db2bin.py b/db2bin.py > index ae5f064..28cd7d2 100755 > --- a/db2bin.py > +++ b/db2bin.py > @@ -1,6 +1,6 @@ > #!/usr/bin/env python > > -from cStringIO import StringIO > +from io import BytesIO, open > import struct > import hashlib > from dbparse import DBParser > @@ -10,21 +10,21 @@ MAGIC = 0x52474442 > VERSION = 19 > > if len(sys.argv) < 3: > - print 'Usage: %s output-file input-file [key-file]' % sys.argv[0] > + print('Usage: %s output-file input-file [key-file]' % sys.argv[0]) > sys.exit(2) > > def create_rules(countries): > result = {} > - for c in countries.itervalues(): > + for c in countries.values(): > for rule in c.permissions: > result[rule] = 1 > - return result.keys() > + return list(result) Here and elsewhere, to get a list of the keys from a dictionary, we use list(dict). Experimentally I find this works, but I haven't been able to find anything which actually tells me that this is the defined behavior, and examples seem to prefer list(dict.keys()). I'm curious why this is guaranteed to provide a lsit of dictionary keys, and why you've done that rather than list(dict.keys()) (I'll grant that the scripts elsewhere use list(dict), so maybe you were just being consistent with that). > def create_collections(countries): > result = {} > - for c in countries.itervalues(): > + for c in countries.values(): > result[c.permissions] = 1 > - return result.keys() > + return list(result) > > > def be32(output, val): > @@ -49,9 +49,9 @@ class PTR(object): > return self._offset > > p = DBParser() > -countries = p.parse(file(sys.argv[2])) > +countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8')) > > -countrynames = countries.keys() > +countrynames = list(countries) > countrynames.sort() > > power = [] > @@ -67,7 +67,7 @@ rules.sort() > collections = create_collections(countries) > collections.sort() > > -output = StringIO() > +output = BytesIO() > > # struct regdb_file_header > be32(output, MAGIC) > @@ -118,7 +118,7 @@ reg_country_ptr.set() > for alpha2 in countrynames: > coll = countries[alpha2] > # struct regdb_file_reg_country > - output.write(struct.pack('>ccxBI', str(alpha2[0]), str(alpha2[1]), coll.dfs_region, reg_rules_collections[coll.permissions])) > + output.write(struct.pack('>BBxBI', alpha2[0], alpha2[1], coll.dfs_region, reg_rules_collections[coll.permissions])) > > > if len(sys.argv) > 3: > @@ -143,5 +143,5 @@ if len(sys.argv) > 3: > else: > siglen.set(0) > > -outfile = open(sys.argv[1], 'w') > +outfile = open(sys.argv[1], 'wb') > outfile.write(output.getvalue()) > diff --git a/db2fw.py b/db2fw.py > index 630e4d6..91b88d3 100755 > --- a/db2fw.py > +++ b/db2fw.py > @@ -1,6 +1,6 @@ > #!/usr/bin/env python > > -from cStringIO import StringIO > +from io import BytesIO, open > import struct > import hashlib > from dbparse import DBParser > @@ -10,21 +10,21 @@ MAGIC = 0x52474442 > VERSION = 20 > > if len(sys.argv) < 3: > - print 'Usage: %s output-file input-file' % sys.argv[0] > + print('Usage: %s output-file input-file' % sys.argv[0]) > sys.exit(2) > > def create_rules(countries): > result = {} > - for c in countries.itervalues(): > + for c in countries.values(): > for rule in c.permissions: > result[rule] = 1 > - return result.keys() > + return list(result) > > def create_collections(countries): > result = {} > - for c in countries.itervalues(): > + for c in countries.values(): > result[(c.permissions, c.dfs_region)] = 1 > - return result.keys() > + return list(result) > > > def be32(output, val): > @@ -58,26 +58,26 @@ class PTR(object): > return self._written > > p = DBParser() > -countries = p.parse(file(sys.argv[2])) > +countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8')) > rules = create_rules(countries) > rules.sort() > collections = create_collections(countries) > collections.sort() > > -output = StringIO() > +output = BytesIO() > > # struct regdb_file_header > be32(output, MAGIC) > be32(output, VERSION) > > country_ptrs = {} > -countrynames = countries.keys() > +countrynames = list(countries) > countrynames.sort() > for alpha2 in countrynames: > coll = countries[alpha2] > - output.write(struct.pack('>cc', str(alpha2[0]), str(alpha2[1]))) > + output.write(struct.pack('>BB', alpha2[0], alpha2[1])) > country_ptrs[alpha2] = PTR(output) > -output.write('\x00' * 4) > +output.write(b'\x00' * 4) > > reg_rules = {} > flags = 0 > @@ -104,8 +104,8 @@ for reg_rule in rules: > cac_timeout = 0 > if cac_timeout: > rule_len += 2 > - output.write(struct.pack('>BBHIII', rule_len, flags, power_rule.max_eirp * 100, > - freq_range.start * 1000, freq_range.end * 1000, freq_range.maxbw * 1000, > + output.write(struct.pack('>BBHIII', rule_len, flags, int(power_rule.max_eirp * 100), > + int(freq_range.start * 1000), int(freq_range.end * 1000), int(freq_range.maxbw * 1000), > )) > if cac_timeout: > output.write(struct.pack('>H', cac_timeout)) > @@ -129,5 +129,5 @@ for coll in collections: > for alpha2 in countrynames: > assert country_ptrs[alpha2].written > > -outfile = open(sys.argv[1], 'w') > +outfile = open(sys.argv[1], 'wb') > outfile.write(output.getvalue()) > diff --git a/dbparse.py b/dbparse.py > index b735b6a..d73d1bd 100755 > --- a/dbparse.py > +++ b/dbparse.py > @@ -1,5 +1,7 @@ > #!/usr/bin/env python > > +from builtins import bytes > +from functools import total_ordering > import sys, math > > # must match <linux/nl80211.h> enum nl80211_reg_rule_flags > @@ -25,6 +27,7 @@ dfs_regions = { > 'DFS-JP': 3, > } > > +@total_ordering > class FreqBand(object): > def __init__(self, start, end, bw, comments=None): > self.start = start > @@ -32,41 +35,49 @@ class FreqBand(object): > self.maxbw = bw > self.comments = comments or [] > > - def __cmp__(self, other): > - s = self > - o = other > - if not isinstance(o, FreqBand): > - return False > - return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw)) > + def _as_tuple(self): > + return (self.start, self.end, self.maxbw) > + > + def __eq__(self, other): > + return (self._as_tuple() == other._as_tuple()) > + > + def __ne__(self, other): > + return not (self == other) > + > + def __lt__(self, other): > + return (self._as_tuple() < other._as_tuple()) > > def __hash__(self): > - s = self > - return hash((s.start, s.end, s.maxbw)) > + return hash(self._as_tuple()) > > def __str__(self): > return '<FreqBand %.3f - %.3f @ %.3f>' % ( > self.start, self.end, self.maxbw) > > +@total_ordering > class PowerRestriction(object): > def __init__(self, max_ant_gain, max_eirp, comments = None): > self.max_ant_gain = max_ant_gain > self.max_eirp = max_eirp > self.comments = comments or [] > > - def __cmp__(self, other): > - s = self > - o = other > - if not isinstance(o, PowerRestriction): > - return False > - return cmp((s.max_ant_gain, s.max_eirp), > - (o.max_ant_gain, o.max_eirp)) > + def _as_tuple(self): > + return (self.max_ant_gain, self.max_eirp) > > - def __str__(self): > - return '<PowerRestriction ...>' > + def __eq__(self, other): > + return (self._as_tuple() == other._as_tuple()) > + > + def __ne__(self, other): > + return not (self == other) > + > + def __lt__(self, other): > + return (self._as_tuple() < other._as_tuple()) > > def __hash__(self): > - s = self > - return hash((s.max_ant_gain, s.max_eirp)) > + return hash(self._as_tuple()) > + > + def __str__(self): > + return '<PowerRestriction ...>' > > class DFSRegionError(Exception): > def __init__(self, dfs_region): > @@ -76,6 +87,7 @@ class FlagError(Exception): > def __init__(self, flag): > self.flag = flag > > +@total_ordering > class Permission(object): > def __init__(self, freqband, power, flags): > assert isinstance(freqband, FreqBand) > @@ -92,10 +104,14 @@ class Permission(object): > def _as_tuple(self): > return (self.freqband, self.power, self.flags) > > - def __cmp__(self, other): > - if not isinstance(other, Permission): > - return False > - return cmp(self._as_tuple(), other._as_tuple()) > + def __eq__(self, other): > + return (self._as_tuple() == other._as_tuple()) > + > + def __ne__(self, other): > + return not (self == other) > + > + def __lt__(self, other): > + return (self._as_tuple() < other._as_tuple()) > > def __hash__(self): > return hash(self._as_tuple()) > @@ -104,12 +120,12 @@ class Country(object): > def __init__(self, dfs_region, permissions=None, comments=None): > self._permissions = permissions or [] > self.comments = comments or [] > - self.dfs_region = 0 > + self.dfs_region = 0 > > - if dfs_region: > - if not dfs_region in dfs_regions: > - raise DFSRegionError(dfs_region) > - self.dfs_region = dfs_regions[dfs_region] > + if dfs_region: > + if not dfs_region in dfs_regions: > + raise DFSRegionError(dfs_region) > + self.dfs_region = dfs_regions[dfs_region] > > def add(self, perm): > assert isinstance(perm, Permission) > @@ -248,6 +264,7 @@ class DBParser(object): > for cname in cnames: > if len(cname) != 2: > self._warn("country '%s' not alpha2" % cname) > + cname = bytes(cname, 'ascii') > if not cname in self._countries: > self._countries[cname] = Country(dfs_region, comments=self._comments) > self._current_countries[cname] = self._countries[cname] > @@ -304,9 +321,9 @@ class DBParser(object): > p = self._power[pname] > try: > perm = Permission(b, p, flags) > - except FlagError, e: > + except FlagError as e: > self._syntax_error("Invalid flag '%s'" % e.flag) > - for cname, c in self._current_countries.iteritems(): > + for cname, c in self._current_countries.items(): > if perm in c: > self._warn('Rule "%s, %s" added to "%s" twice' % ( > bname, pname, cname)) > @@ -360,7 +377,7 @@ class DBParser(object): > > countries = self._countries > bands = {} > - for k, v in self._bands.iteritems(): > + for k, v in self._bands.items(): > if k in self._bands_used: > bands[self._banddup[k]] = v > continue > @@ -369,7 +386,7 @@ class DBParser(object): > self._lineno = self._bandline[k] > self._warn('Unused band definition "%s"' % k) > power = {} > - for k, v in self._power.iteritems(): > + for k, v in self._power.items(): > if k in self._power_used: > power[self._powerdup[k]] = v > continue > -- > 2.16.1 >
On 03/22/2018 03:05 PM, Seth Forshee wrote: > On Sun, Feb 04, 2018 at 12:36:54AM +0100, Matthias Schiffer wrote: >> When playing with the generation scripts for OpenWrt development, I noticed >> that these scripts still required Python 2. Future-proof them by replacing >> deprecated functions with new Python 3 compatible variants. The result >> works with both Python 2.7 and Python 3.x; older Python 2.x releases are >> not supported anymore. >> >> regulatory.db and regulatory.bin are unchanged and reproducible across >> Python versions. Note that there is no stable release of m2crypto for >> Python 3 yet; I used the current development branch for testing. > > I can't say I'm all that knowledgable about Python 2 to Python 3 > conversion, but as far as I can tell this looks okay. It does seem to > work for me running with both Python 2 and Python 3. > > One question below though, mostly just to satisfy my curiousity. > >> Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net> >> --- >> >> v2: explicitly open input file with UTF-8 encoding; otherwise the scripts >> will fail without a UTF-8 locale set in the environment >> >> >> db2bin.py | 22 ++++++++--------- >> db2fw.py | 28 +++++++++++----------- >> dbparse.py | 81 +++++++++++++++++++++++++++++++++++++------------------------- >> 3 files changed, 74 insertions(+), 57 deletions(-) >> >> diff --git a/db2bin.py b/db2bin.py >> index ae5f064..28cd7d2 100755 >> --- a/db2bin.py >> +++ b/db2bin.py >> @@ -1,6 +1,6 @@ >> #!/usr/bin/env python >> >> -from cStringIO import StringIO >> +from io import BytesIO, open >> import struct >> import hashlib >> from dbparse import DBParser >> @@ -10,21 +10,21 @@ MAGIC = 0x52474442 >> VERSION = 19 >> >> if len(sys.argv) < 3: >> - print 'Usage: %s output-file input-file [key-file]' % sys.argv[0] >> + print('Usage: %s output-file input-file [key-file]' % sys.argv[0]) >> sys.exit(2) >> >> def create_rules(countries): >> result = {} >> - for c in countries.itervalues(): >> + for c in countries.values(): >> for rule in c.permissions: >> result[rule] = 1 >> - return result.keys() >> + return list(result) > > Here and elsewhere, to get a list of the keys from a dictionary, we use > list(dict). Experimentally I find this works, but I haven't been able to > find anything which actually tells me that this is the defined behavior, > and examples seem to prefer list(dict.keys()). I'm curious why this is > guaranteed to provide a lsit of dictionary keys, and why you've done > that rather than list(dict.keys()) (I'll grant that the scripts > elsewhere use list(dict), so maybe you were just being consistent with > that). list(dict) is the recommended syntax in http://python-future.org/compatible_idioms.html#dict-keys-values-items-as-a-list . Regards, Matthias > >> def create_collections(countries): >> result = {} >> - for c in countries.itervalues(): >> + for c in countries.values(): >> result[c.permissions] = 1 >> - return result.keys() >> + return list(result) >> >> >> def be32(output, val): >> @@ -49,9 +49,9 @@ class PTR(object): >> return self._offset >> >> p = DBParser() >> -countries = p.parse(file(sys.argv[2])) >> +countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8')) >> >> -countrynames = countries.keys() >> +countrynames = list(countries) >> countrynames.sort() >> >> power = [] >> @@ -67,7 +67,7 @@ rules.sort() >> collections = create_collections(countries) >> collections.sort() >> >> -output = StringIO() >> +output = BytesIO() >> >> # struct regdb_file_header >> be32(output, MAGIC) >> @@ -118,7 +118,7 @@ reg_country_ptr.set() >> for alpha2 in countrynames: >> coll = countries[alpha2] >> # struct regdb_file_reg_country >> - output.write(struct.pack('>ccxBI', str(alpha2[0]), str(alpha2[1]), coll.dfs_region, reg_rules_collections[coll.permissions])) >> + output.write(struct.pack('>BBxBI', alpha2[0], alpha2[1], coll.dfs_region, reg_rules_collections[coll.permissions])) >> >> >> if len(sys.argv) > 3: >> @@ -143,5 +143,5 @@ if len(sys.argv) > 3: >> else: >> siglen.set(0) >> >> -outfile = open(sys.argv[1], 'w') >> +outfile = open(sys.argv[1], 'wb') >> outfile.write(output.getvalue()) >> diff --git a/db2fw.py b/db2fw.py >> index 630e4d6..91b88d3 100755 >> --- a/db2fw.py >> +++ b/db2fw.py >> @@ -1,6 +1,6 @@ >> #!/usr/bin/env python >> >> -from cStringIO import StringIO >> +from io import BytesIO, open >> import struct >> import hashlib >> from dbparse import DBParser >> @@ -10,21 +10,21 @@ MAGIC = 0x52474442 >> VERSION = 20 >> >> if len(sys.argv) < 3: >> - print 'Usage: %s output-file input-file' % sys.argv[0] >> + print('Usage: %s output-file input-file' % sys.argv[0]) >> sys.exit(2) >> >> def create_rules(countries): >> result = {} >> - for c in countries.itervalues(): >> + for c in countries.values(): >> for rule in c.permissions: >> result[rule] = 1 >> - return result.keys() >> + return list(result) >> >> def create_collections(countries): >> result = {} >> - for c in countries.itervalues(): >> + for c in countries.values(): >> result[(c.permissions, c.dfs_region)] = 1 >> - return result.keys() >> + return list(result) >> >> >> def be32(output, val): >> @@ -58,26 +58,26 @@ class PTR(object): >> return self._written >> >> p = DBParser() >> -countries = p.parse(file(sys.argv[2])) >> +countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8')) >> rules = create_rules(countries) >> rules.sort() >> collections = create_collections(countries) >> collections.sort() >> >> -output = StringIO() >> +output = BytesIO() >> >> # struct regdb_file_header >> be32(output, MAGIC) >> be32(output, VERSION) >> >> country_ptrs = {} >> -countrynames = countries.keys() >> +countrynames = list(countries) >> countrynames.sort() >> for alpha2 in countrynames: >> coll = countries[alpha2] >> - output.write(struct.pack('>cc', str(alpha2[0]), str(alpha2[1]))) >> + output.write(struct.pack('>BB', alpha2[0], alpha2[1])) >> country_ptrs[alpha2] = PTR(output) >> -output.write('\x00' * 4) >> +output.write(b'\x00' * 4) >> >> reg_rules = {} >> flags = 0 >> @@ -104,8 +104,8 @@ for reg_rule in rules: >> cac_timeout = 0 >> if cac_timeout: >> rule_len += 2 >> - output.write(struct.pack('>BBHIII', rule_len, flags, power_rule.max_eirp * 100, >> - freq_range.start * 1000, freq_range.end * 1000, freq_range.maxbw * 1000, >> + output.write(struct.pack('>BBHIII', rule_len, flags, int(power_rule.max_eirp * 100), >> + int(freq_range.start * 1000), int(freq_range.end * 1000), int(freq_range.maxbw * 1000), >> )) >> if cac_timeout: >> output.write(struct.pack('>H', cac_timeout)) >> @@ -129,5 +129,5 @@ for coll in collections: >> for alpha2 in countrynames: >> assert country_ptrs[alpha2].written >> >> -outfile = open(sys.argv[1], 'w') >> +outfile = open(sys.argv[1], 'wb') >> outfile.write(output.getvalue()) >> diff --git a/dbparse.py b/dbparse.py >> index b735b6a..d73d1bd 100755 >> --- a/dbparse.py >> +++ b/dbparse.py >> @@ -1,5 +1,7 @@ >> #!/usr/bin/env python >> >> +from builtins import bytes >> +from functools import total_ordering >> import sys, math >> >> # must match <linux/nl80211.h> enum nl80211_reg_rule_flags >> @@ -25,6 +27,7 @@ dfs_regions = { >> 'DFS-JP': 3, >> } >> >> +@total_ordering >> class FreqBand(object): >> def __init__(self, start, end, bw, comments=None): >> self.start = start >> @@ -32,41 +35,49 @@ class FreqBand(object): >> self.maxbw = bw >> self.comments = comments or [] >> >> - def __cmp__(self, other): >> - s = self >> - o = other >> - if not isinstance(o, FreqBand): >> - return False >> - return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw)) >> + def _as_tuple(self): >> + return (self.start, self.end, self.maxbw) >> + >> + def __eq__(self, other): >> + return (self._as_tuple() == other._as_tuple()) >> + >> + def __ne__(self, other): >> + return not (self == other) >> + >> + def __lt__(self, other): >> + return (self._as_tuple() < other._as_tuple()) >> >> def __hash__(self): >> - s = self >> - return hash((s.start, s.end, s.maxbw)) >> + return hash(self._as_tuple()) >> >> def __str__(self): >> return '<FreqBand %.3f - %.3f @ %.3f>' % ( >> self.start, self.end, self.maxbw) >> >> +@total_ordering >> class PowerRestriction(object): >> def __init__(self, max_ant_gain, max_eirp, comments = None): >> self.max_ant_gain = max_ant_gain >> self.max_eirp = max_eirp >> self.comments = comments or [] >> >> - def __cmp__(self, other): >> - s = self >> - o = other >> - if not isinstance(o, PowerRestriction): >> - return False >> - return cmp((s.max_ant_gain, s.max_eirp), >> - (o.max_ant_gain, o.max_eirp)) >> + def _as_tuple(self): >> + return (self.max_ant_gain, self.max_eirp) >> >> - def __str__(self): >> - return '<PowerRestriction ...>' >> + def __eq__(self, other): >> + return (self._as_tuple() == other._as_tuple()) >> + >> + def __ne__(self, other): >> + return not (self == other) >> + >> + def __lt__(self, other): >> + return (self._as_tuple() < other._as_tuple()) >> >> def __hash__(self): >> - s = self >> - return hash((s.max_ant_gain, s.max_eirp)) >> + return hash(self._as_tuple()) >> + >> + def __str__(self): >> + return '<PowerRestriction ...>' >> >> class DFSRegionError(Exception): >> def __init__(self, dfs_region): >> @@ -76,6 +87,7 @@ class FlagError(Exception): >> def __init__(self, flag): >> self.flag = flag >> >> +@total_ordering >> class Permission(object): >> def __init__(self, freqband, power, flags): >> assert isinstance(freqband, FreqBand) >> @@ -92,10 +104,14 @@ class Permission(object): >> def _as_tuple(self): >> return (self.freqband, self.power, self.flags) >> >> - def __cmp__(self, other): >> - if not isinstance(other, Permission): >> - return False >> - return cmp(self._as_tuple(), other._as_tuple()) >> + def __eq__(self, other): >> + return (self._as_tuple() == other._as_tuple()) >> + >> + def __ne__(self, other): >> + return not (self == other) >> + >> + def __lt__(self, other): >> + return (self._as_tuple() < other._as_tuple()) >> >> def __hash__(self): >> return hash(self._as_tuple()) >> @@ -104,12 +120,12 @@ class Country(object): >> def __init__(self, dfs_region, permissions=None, comments=None): >> self._permissions = permissions or [] >> self.comments = comments or [] >> - self.dfs_region = 0 >> + self.dfs_region = 0 >> >> - if dfs_region: >> - if not dfs_region in dfs_regions: >> - raise DFSRegionError(dfs_region) >> - self.dfs_region = dfs_regions[dfs_region] >> + if dfs_region: >> + if not dfs_region in dfs_regions: >> + raise DFSRegionError(dfs_region) >> + self.dfs_region = dfs_regions[dfs_region] >> >> def add(self, perm): >> assert isinstance(perm, Permission) >> @@ -248,6 +264,7 @@ class DBParser(object): >> for cname in cnames: >> if len(cname) != 2: >> self._warn("country '%s' not alpha2" % cname) >> + cname = bytes(cname, 'ascii') >> if not cname in self._countries: >> self._countries[cname] = Country(dfs_region, comments=self._comments) >> self._current_countries[cname] = self._countries[cname] >> @@ -304,9 +321,9 @@ class DBParser(object): >> p = self._power[pname] >> try: >> perm = Permission(b, p, flags) >> - except FlagError, e: >> + except FlagError as e: >> self._syntax_error("Invalid flag '%s'" % e.flag) >> - for cname, c in self._current_countries.iteritems(): >> + for cname, c in self._current_countries.items(): >> if perm in c: >> self._warn('Rule "%s, %s" added to "%s" twice' % ( >> bname, pname, cname)) >> @@ -360,7 +377,7 @@ class DBParser(object): >> >> countries = self._countries >> bands = {} >> - for k, v in self._bands.iteritems(): >> + for k, v in self._bands.items(): >> if k in self._bands_used: >> bands[self._banddup[k]] = v >> continue >> @@ -369,7 +386,7 @@ class DBParser(object): >> self._lineno = self._bandline[k] >> self._warn('Unused band definition "%s"' % k) >> power = {} >> - for k, v in self._power.iteritems(): >> + for k, v in self._power.items(): >> if k in self._power_used: >> power[self._powerdup[k]] = v >> continue >> -- >> 2.16.1 >>
On Thu, Mar 22, 2018 at 03:44:00PM +0100, Matthias Schiffer wrote: > On 03/22/2018 03:05 PM, Seth Forshee wrote: > > On Sun, Feb 04, 2018 at 12:36:54AM +0100, Matthias Schiffer wrote: > >> When playing with the generation scripts for OpenWrt development, I noticed > >> that these scripts still required Python 2. Future-proof them by replacing > >> deprecated functions with new Python 3 compatible variants. The result > >> works with both Python 2.7 and Python 3.x; older Python 2.x releases are > >> not supported anymore. > >> > >> regulatory.db and regulatory.bin are unchanged and reproducible across > >> Python versions. Note that there is no stable release of m2crypto for > >> Python 3 yet; I used the current development branch for testing. > > > > I can't say I'm all that knowledgable about Python 2 to Python 3 > > conversion, but as far as I can tell this looks okay. It does seem to > > work for me running with both Python 2 and Python 3. > > > > One question below though, mostly just to satisfy my curiousity. > > > >> Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net> > >> --- > >> > >> v2: explicitly open input file with UTF-8 encoding; otherwise the scripts > >> will fail without a UTF-8 locale set in the environment > >> > >> > >> db2bin.py | 22 ++++++++--------- > >> db2fw.py | 28 +++++++++++----------- > >> dbparse.py | 81 +++++++++++++++++++++++++++++++++++++------------------------- > >> 3 files changed, 74 insertions(+), 57 deletions(-) > >> > >> diff --git a/db2bin.py b/db2bin.py > >> index ae5f064..28cd7d2 100755 > >> --- a/db2bin.py > >> +++ b/db2bin.py > >> @@ -1,6 +1,6 @@ > >> #!/usr/bin/env python > >> > >> -from cStringIO import StringIO > >> +from io import BytesIO, open > >> import struct > >> import hashlib > >> from dbparse import DBParser > >> @@ -10,21 +10,21 @@ MAGIC = 0x52474442 > >> VERSION = 19 > >> > >> if len(sys.argv) < 3: > >> - print 'Usage: %s output-file input-file [key-file]' % sys.argv[0] > >> + print('Usage: %s output-file input-file [key-file]' % sys.argv[0]) > >> sys.exit(2) > >> > >> def create_rules(countries): > >> result = {} > >> - for c in countries.itervalues(): > >> + for c in countries.values(): > >> for rule in c.permissions: > >> result[rule] = 1 > >> - return result.keys() > >> + return list(result) > > > > Here and elsewhere, to get a list of the keys from a dictionary, we use > > list(dict). Experimentally I find this works, but I haven't been able to > > find anything which actually tells me that this is the defined behavior, > > and examples seem to prefer list(dict.keys()). I'm curious why this is > > guaranteed to provide a lsit of dictionary keys, and why you've done > > that rather than list(dict.keys()) (I'll grant that the scripts > > elsewhere use list(dict), so maybe you were just being consistent with > > that). > > list(dict) is the recommended syntax in > http://python-future.org/compatible_idioms.html#dict-keys-values-items-as-a-list Thanks for the explanation. I've applied both patches. Seth
diff --git a/db2bin.py b/db2bin.py index ae5f064..28cd7d2 100755 --- a/db2bin.py +++ b/db2bin.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from cStringIO import StringIO +from io import BytesIO, open import struct import hashlib from dbparse import DBParser @@ -10,21 +10,21 @@ MAGIC = 0x52474442 VERSION = 19 if len(sys.argv) < 3: - print 'Usage: %s output-file input-file [key-file]' % sys.argv[0] + print('Usage: %s output-file input-file [key-file]' % sys.argv[0]) sys.exit(2) def create_rules(countries): result = {} - for c in countries.itervalues(): + for c in countries.values(): for rule in c.permissions: result[rule] = 1 - return result.keys() + return list(result) def create_collections(countries): result = {} - for c in countries.itervalues(): + for c in countries.values(): result[c.permissions] = 1 - return result.keys() + return list(result) def be32(output, val): @@ -49,9 +49,9 @@ class PTR(object): return self._offset p = DBParser() -countries = p.parse(file(sys.argv[2])) +countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8')) -countrynames = countries.keys() +countrynames = list(countries) countrynames.sort() power = [] @@ -67,7 +67,7 @@ rules.sort() collections = create_collections(countries) collections.sort() -output = StringIO() +output = BytesIO() # struct regdb_file_header be32(output, MAGIC) @@ -118,7 +118,7 @@ reg_country_ptr.set() for alpha2 in countrynames: coll = countries[alpha2] # struct regdb_file_reg_country - output.write(struct.pack('>ccxBI', str(alpha2[0]), str(alpha2[1]), coll.dfs_region, reg_rules_collections[coll.permissions])) + output.write(struct.pack('>BBxBI', alpha2[0], alpha2[1], coll.dfs_region, reg_rules_collections[coll.permissions])) if len(sys.argv) > 3: @@ -143,5 +143,5 @@ if len(sys.argv) > 3: else: siglen.set(0) -outfile = open(sys.argv[1], 'w') +outfile = open(sys.argv[1], 'wb') outfile.write(output.getvalue()) diff --git a/db2fw.py b/db2fw.py index 630e4d6..91b88d3 100755 --- a/db2fw.py +++ b/db2fw.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from cStringIO import StringIO +from io import BytesIO, open import struct import hashlib from dbparse import DBParser @@ -10,21 +10,21 @@ MAGIC = 0x52474442 VERSION = 20 if len(sys.argv) < 3: - print 'Usage: %s output-file input-file' % sys.argv[0] + print('Usage: %s output-file input-file' % sys.argv[0]) sys.exit(2) def create_rules(countries): result = {} - for c in countries.itervalues(): + for c in countries.values(): for rule in c.permissions: result[rule] = 1 - return result.keys() + return list(result) def create_collections(countries): result = {} - for c in countries.itervalues(): + for c in countries.values(): result[(c.permissions, c.dfs_region)] = 1 - return result.keys() + return list(result) def be32(output, val): @@ -58,26 +58,26 @@ class PTR(object): return self._written p = DBParser() -countries = p.parse(file(sys.argv[2])) +countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8')) rules = create_rules(countries) rules.sort() collections = create_collections(countries) collections.sort() -output = StringIO() +output = BytesIO() # struct regdb_file_header be32(output, MAGIC) be32(output, VERSION) country_ptrs = {} -countrynames = countries.keys() +countrynames = list(countries) countrynames.sort() for alpha2 in countrynames: coll = countries[alpha2] - output.write(struct.pack('>cc', str(alpha2[0]), str(alpha2[1]))) + output.write(struct.pack('>BB', alpha2[0], alpha2[1])) country_ptrs[alpha2] = PTR(output) -output.write('\x00' * 4) +output.write(b'\x00' * 4) reg_rules = {} flags = 0 @@ -104,8 +104,8 @@ for reg_rule in rules: cac_timeout = 0 if cac_timeout: rule_len += 2 - output.write(struct.pack('>BBHIII', rule_len, flags, power_rule.max_eirp * 100, - freq_range.start * 1000, freq_range.end * 1000, freq_range.maxbw * 1000, + output.write(struct.pack('>BBHIII', rule_len, flags, int(power_rule.max_eirp * 100), + int(freq_range.start * 1000), int(freq_range.end * 1000), int(freq_range.maxbw * 1000), )) if cac_timeout: output.write(struct.pack('>H', cac_timeout)) @@ -129,5 +129,5 @@ for coll in collections: for alpha2 in countrynames: assert country_ptrs[alpha2].written -outfile = open(sys.argv[1], 'w') +outfile = open(sys.argv[1], 'wb') outfile.write(output.getvalue()) diff --git a/dbparse.py b/dbparse.py index b735b6a..d73d1bd 100755 --- a/dbparse.py +++ b/dbparse.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +from builtins import bytes +from functools import total_ordering import sys, math # must match <linux/nl80211.h> enum nl80211_reg_rule_flags @@ -25,6 +27,7 @@ dfs_regions = { 'DFS-JP': 3, } +@total_ordering class FreqBand(object): def __init__(self, start, end, bw, comments=None): self.start = start @@ -32,41 +35,49 @@ class FreqBand(object): self.maxbw = bw self.comments = comments or [] - def __cmp__(self, other): - s = self - o = other - if not isinstance(o, FreqBand): - return False - return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw)) + def _as_tuple(self): + return (self.start, self.end, self.maxbw) + + def __eq__(self, other): + return (self._as_tuple() == other._as_tuple()) + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return (self._as_tuple() < other._as_tuple()) def __hash__(self): - s = self - return hash((s.start, s.end, s.maxbw)) + return hash(self._as_tuple()) def __str__(self): return '<FreqBand %.3f - %.3f @ %.3f>' % ( self.start, self.end, self.maxbw) +@total_ordering class PowerRestriction(object): def __init__(self, max_ant_gain, max_eirp, comments = None): self.max_ant_gain = max_ant_gain self.max_eirp = max_eirp self.comments = comments or [] - def __cmp__(self, other): - s = self - o = other - if not isinstance(o, PowerRestriction): - return False - return cmp((s.max_ant_gain, s.max_eirp), - (o.max_ant_gain, o.max_eirp)) + def _as_tuple(self): + return (self.max_ant_gain, self.max_eirp) - def __str__(self): - return '<PowerRestriction ...>' + def __eq__(self, other): + return (self._as_tuple() == other._as_tuple()) + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return (self._as_tuple() < other._as_tuple()) def __hash__(self): - s = self - return hash((s.max_ant_gain, s.max_eirp)) + return hash(self._as_tuple()) + + def __str__(self): + return '<PowerRestriction ...>' class DFSRegionError(Exception): def __init__(self, dfs_region): @@ -76,6 +87,7 @@ class FlagError(Exception): def __init__(self, flag): self.flag = flag +@total_ordering class Permission(object): def __init__(self, freqband, power, flags): assert isinstance(freqband, FreqBand) @@ -92,10 +104,14 @@ class Permission(object): def _as_tuple(self): return (self.freqband, self.power, self.flags) - def __cmp__(self, other): - if not isinstance(other, Permission): - return False - return cmp(self._as_tuple(), other._as_tuple()) + def __eq__(self, other): + return (self._as_tuple() == other._as_tuple()) + + def __ne__(self, other): + return not (self == other) + + def __lt__(self, other): + return (self._as_tuple() < other._as_tuple()) def __hash__(self): return hash(self._as_tuple()) @@ -104,12 +120,12 @@ class Country(object): def __init__(self, dfs_region, permissions=None, comments=None): self._permissions = permissions or [] self.comments = comments or [] - self.dfs_region = 0 + self.dfs_region = 0 - if dfs_region: - if not dfs_region in dfs_regions: - raise DFSRegionError(dfs_region) - self.dfs_region = dfs_regions[dfs_region] + if dfs_region: + if not dfs_region in dfs_regions: + raise DFSRegionError(dfs_region) + self.dfs_region = dfs_regions[dfs_region] def add(self, perm): assert isinstance(perm, Permission) @@ -248,6 +264,7 @@ class DBParser(object): for cname in cnames: if len(cname) != 2: self._warn("country '%s' not alpha2" % cname) + cname = bytes(cname, 'ascii') if not cname in self._countries: self._countries[cname] = Country(dfs_region, comments=self._comments) self._current_countries[cname] = self._countries[cname] @@ -304,9 +321,9 @@ class DBParser(object): p = self._power[pname] try: perm = Permission(b, p, flags) - except FlagError, e: + except FlagError as e: self._syntax_error("Invalid flag '%s'" % e.flag) - for cname, c in self._current_countries.iteritems(): + for cname, c in self._current_countries.items(): if perm in c: self._warn('Rule "%s, %s" added to "%s" twice' % ( bname, pname, cname)) @@ -360,7 +377,7 @@ class DBParser(object): countries = self._countries bands = {} - for k, v in self._bands.iteritems(): + for k, v in self._bands.items(): if k in self._bands_used: bands[self._banddup[k]] = v continue @@ -369,7 +386,7 @@ class DBParser(object): self._lineno = self._bandline[k] self._warn('Unused band definition "%s"' % k) power = {} - for k, v in self._power.iteritems(): + for k, v in self._power.items(): if k in self._power_used: power[self._powerdup[k]] = v continue
When playing with the generation scripts for OpenWrt development, I noticed that these scripts still required Python 2. Future-proof them by replacing deprecated functions with new Python 3 compatible variants. The result works with both Python 2.7 and Python 3.x; older Python 2.x releases are not supported anymore. regulatory.db and regulatory.bin are unchanged and reproducible across Python versions. Note that there is no stable release of m2crypto for Python 3 yet; I used the current development branch for testing. Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net> --- v2: explicitly open input file with UTF-8 encoding; otherwise the scripts will fail without a UTF-8 locale set in the environment db2bin.py | 22 ++++++++--------- db2fw.py | 28 +++++++++++----------- dbparse.py | 81 +++++++++++++++++++++++++++++++++++++------------------------- 3 files changed, 74 insertions(+), 57 deletions(-)