[wireless-regdb] [PATCH v2 2/2] wireless-regdb: make scripts compatible with Python 3
Seth Forshee
seth.forshee at canonical.com
Thu Mar 22 07:05:44 PDT 2018
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 at 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,
> }
>
> + at 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)
>
> + at 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
>
> + at 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
>
More information about the wireless-regdb
mailing list