docs/html/
# files generated by tests
-tests/getsig/tmp/
+tmp/
test:
script:
- apt-get update
- - apt-get install -y python-dev gcc
+ - apt-get install -y python3-dev gcc
+ - pip3 install -e .
- cd tests
- ./complete-ci-tests
### Installing
-Note that only Python 2 is supported. We recommend version 2.7.7 or
-later.
+Note that only Python 3 is supported. We recommend version 3.4 or later.
The easiest way to install the `fdroidserver` tools is on Ubuntu, Mint or other
Ubuntu based distributions, you can install using:
Python's `pip` also works:
- sudo pip install fdroidserver
+ sudo pip3 install fdroidserver
The combination of `virtualenv` and `pip` is great for testing out the
latest versions of `fdroidserver`. Using `pip`, `fdroidserver` can
For Debian based distributions:
- apt-get install python-dev python-pip python-virtualenv
+ apt-get install python3-dev python3-pip virtualenv
Then here's how to install:
cd fdroidserver
virtualenv env/
source env/bin/activate
- pip install -e .
- python2 setup.py install
+ pip3 install -e .
+ python3 setup.py install
@item
GNU/Linux
@item
-Python 2.x
-To be sure of being able to process all apk files without error, you need
-2.7.7 or later. See @code{http://bugs.python.org/issue14315}.
+Python 3.4 or later
@item
The Android SDK Tools and Build-tools.
Note that F-Droid does not assume that you have the Android SDK in your
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# Copy this file to config.py, then amend the settings below according to
# your system configuration.
-#!/usr/bin/env python2
+#!/usr/bin/env python3
#
# You may want to alter these before running ./makebuildserver
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# fdroid.py - part of the FDroid server tools
# Copyright (C) 2010-2015, Ciaran Gultnieks, ciaran@ciarang.com
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# build.py - part of the FDroid server tools
# Copyright (C) 2010-2014, Ciaran Gultnieks, ciaran@ciarang.com
import traceback
import time
import json
-from ConfigParser import ConfigParser
+from configparser import ConfigParser
from argparse import ArgumentParser
import logging
-import common
-import net
-import metadata
-import scanner
-from common import FDroidException, BuildException, VCSException, FDroidPopen, SdkToolsPopen
+from . import common
+from . import net
+from . import metadata
+from . import scanner
+from .common import FDroidException, BuildException, VCSException, FDroidPopen, SdkToolsPopen
try:
import paramiko
if not ndk_path:
logging.critical("Android NDK version '%s' could not be found!" % build.ndk or 'r10e')
logging.critical("Configured versions:")
- for k, v in config['ndk_paths'].iteritems():
+ for k, v in config['ndk_paths'].items():
if k.endswith("_orig"):
continue
logging.critical(" %s: %s" % (k, v))
raise FDroidException("No apps to process.")
if options.latest:
- for app in apps.itervalues():
+ for app in apps.values():
for build in reversed(app.builds):
if build.disable and not options.force:
continue
# Build applications...
failed_apps = {}
build_succeeded = []
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
first = True
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# checkupdates.py - part of the FDroid server tools
# Copyright (C) 2010-2015, Ciaran Gultnieks, ciaran@ciarang.com
import sys
import os
import re
-import urllib2
+import urllib.request
+import urllib.error
import time
import subprocess
from argparse import ArgumentParser
import traceback
-import HTMLParser
+from html.parser import HTMLParser
from distutils.version import LooseVersion
import logging
import copy
-import common
-import metadata
-from common import VCSException, FDroidException
-from metadata import MetaDataException
+from . import common
+from . import metadata
+from .common import VCSException, FDroidException
+from .metadata import MetaDataException
# Check for a new version by looking at a document retrieved via HTTP.
vercode = "99999999"
if len(urlcode) > 0:
logging.debug("...requesting {0}".format(urlcode))
- req = urllib2.Request(urlcode, None)
- resp = urllib2.urlopen(req, None, 20)
+ req = urllib.request.Request(urlcode, None)
+ resp = urllib.request.urlopen(req, None, 20)
page = resp.read()
m = re.search(codeex, page)
if len(urlver) > 0:
if urlver != '.':
logging.debug("...requesting {0}".format(urlver))
- req = urllib2.Request(urlver, None)
- resp = urllib2.urlopen(req, None, 20)
+ req = urllib.request.Request(urlver, None)
+ resp = urllib.request.urlopen(req, None, 20)
page = resp.read()
m = re.search(verex, page)
time.sleep(15)
url = 'https://play.google.com/store/apps/details?id=' + app.id
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux i686; rv:18.0) Gecko/20100101 Firefox/18.0'}
- req = urllib2.Request(url, None, headers)
+ req = urllib.request.Request(url, None, headers)
try:
- resp = urllib2.urlopen(req, None, 20)
+ resp = urllib.request.urlopen(req, None, 20)
page = resp.read()
- except urllib2.HTTPError as e:
+ except urllib.error.HTTPError as e:
return (None, str(e.code))
except Exception as e:
return (None, 'Failed:' + str(e))
m = re.search('itemprop="softwareVersion">[ ]*([^<]+)[ ]*</div>', page)
if m:
- html_parser = HTMLParser.HTMLParser()
+ html_parser = HTMLParser()
version = html_parser.unescape(m.group(1))
if version == 'Varies with device':
.format(common.getappname(app), version))
return
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
if options.autoonly and app.AutoUpdateMode in ('None', 'Static'):
logging.debug("Nothing to do for {0}...".format(appid))
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# common.py - part of the FDroid server tools
# Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
# common.py is imported by all modules, so do not import third-party
# libraries here as they will become a requirement for all commands.
+import io
import os
import sys
import re
import logging
import hashlib
import socket
+import base64
import xml.etree.ElementTree as XMLElementTree
-try:
- # Python 2
- from Queue import Queue
-except ImportError:
- # Python 3
- from queue import Queue
+from queue import Queue
from zipfile import ZipFile
-import metadata
-from fdroidserver.asynchronousfilereader import AsynchronousFileReader
+import fdroidserver.metadata
+from .asynchronousfilereader import AsynchronousFileReader
XMLElementTree.register_namespace('android', 'http://schemas.android.com/apk/res/android')
config = {}
logging.debug("Reading %s" % config_file)
- execfile(config_file, config)
+ with io.open(config_file, "rb") as f:
+ code = compile(f.read(), config_file, 'exec')
+ exec(code, None, config)
# smartcardoptions must be a list since its command line args for Popen
if 'smartcardoptions' in config:
config[k] = clean_description(config[k])
if 'serverwebroot' in config:
- if isinstance(config['serverwebroot'], basestring):
+ if isinstance(config['serverwebroot'], str):
roots = [config['serverwebroot']]
- elif all(isinstance(item, basestring) for item in config['serverwebroot']):
+ elif all(isinstance(item, str) for item in config['serverwebroot']):
roots = config['serverwebroot']
else:
raise TypeError('only accepts strings, lists, and tuples')
filename = '.fdroid.' + pwtype + '.txt'
fd = os.open(filename, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, 0o600)
if password is None:
- os.write(fd, config[pwtype])
+ os.write(fd, config[pwtype].encode('utf-8'))
else:
- os.write(fd, password)
+ os.write(fd, password.encode('utf-8'))
os.close(fd)
config[pwtype + 'file'] = filename
return allapps
apps = {}
- for appid, app in allapps.iteritems():
+ for appid, app in allapps.items():
if appid in vercodes:
apps[appid] = app
raise FDroidException("No packages specified")
error = False
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
vc = vercodes[appid]
if not vc:
continue
def getsrclibvcs(name):
- if name not in metadata.srclibs:
+ if name not in fdroidserver.metadata.srclibs:
raise VCSException("Missing srclib " + name)
- return metadata.srclibs[name]['Repo Type']
+ return fdroidserver.metadata.srclibs[name]['Repo Type']
class vcs:
# automatically if either of those things changes.
fdpath = os.path.join(self.local, '..',
'.fdroidvcs-' + os.path.basename(self.local))
+ fdpath = os.path.normpath(fdpath)
cdata = self.repotype() + ' ' + self.remote
writeback = True
deleterepo = False
# If necessary, write the .fdroidvcs file.
if writeback and not self.clone_failed:
- with open(fdpath, 'w') as f:
+ os.makedirs(os.path.dirname(fdpath), exist_ok=True)
+ with open(fdpath, 'w+') as f:
f.write(cdata)
if exc is not None:
if element.text is None:
return ""
s = XMLElementTree.tostring(element, encoding='utf-8', method='text')
- return s.strip()
+ return s.decode('utf-8').strip()
for path in xmlfiles:
if not os.path.isfile(path):
continue
if "{http://schemas.android.com/apk/res/android}label" not in app.attrib:
continue
- label = app.attrib["{http://schemas.android.com/apk/res/android}label"].encode('utf-8')
+ label = app.attrib["{http://schemas.android.com/apk/res/android}label"]
result = retrieve_string_singleline(app_dir, label)
if result:
result = result.strip()
proppath = os.path.join(root_dir, 'project.properties')
if not os.path.isfile(proppath):
return libraries
- for line in file(proppath):
- if not line.startswith('android.library.reference.'):
- continue
- path = line.split('=')[1].strip()
- relpath = os.path.join(root_dir, path)
- if not os.path.isdir(relpath):
- continue
- logging.debug("Found subproject at %s" % path)
- libraries.append(path)
+ with open(proppath, 'r') as f:
+ for line in f:
+ if not line.startswith('android.library.reference.'):
+ continue
+ path = line.split('=')[1].strip()
+ relpath = os.path.join(root_dir, path)
+ if not os.path.isdir(relpath):
+ continue
+ logging.debug("Found subproject at %s" % path)
+ libraries.append(path)
return libraries
package = None
if gradle:
- for line in file(path):
- if gradle_comment.match(line):
- continue
- # Grab first occurence of each to avoid running into
- # alternative flavours and builds.
- if not package:
- matches = psearch_g(line)
- if matches:
- s = matches.group(2)
- if app_matches_packagename(app, s):
- package = s
- if not version:
- matches = vnsearch_g(line)
- if matches:
- version = matches.group(2)
- if not vercode:
- matches = vcsearch_g(line)
- if matches:
- vercode = matches.group(1)
+ with open(path, 'r') as f:
+ for line in f:
+ if gradle_comment.match(line):
+ continue
+ # Grab first occurence of each to avoid running into
+ # alternative flavours and builds.
+ if not package:
+ matches = psearch_g(line)
+ if matches:
+ s = matches.group(2)
+ if app_matches_packagename(app, s):
+ package = s
+ if not version:
+ matches = vnsearch_g(line)
+ if matches:
+ version = matches.group(2)
+ if not vercode:
+ matches = vcsearch_g(line)
+ if matches:
+ vercode = matches.group(1)
else:
try:
xml = parse_xml(path)
if "package" in xml.attrib:
- s = xml.attrib["package"].encode('utf-8')
+ s = xml.attrib["package"]
if app_matches_packagename(app, s):
package = s
if "{http://schemas.android.com/apk/res/android}versionName" in xml.attrib:
- version = xml.attrib["{http://schemas.android.com/apk/res/android}versionName"].encode('utf-8')
+ version = xml.attrib["{http://schemas.android.com/apk/res/android}versionName"]
base_dir = os.path.dirname(path)
version = retrieve_string_singleline(base_dir, version)
if "{http://schemas.android.com/apk/res/android}versionCode" in xml.attrib:
- a = xml.attrib["{http://schemas.android.com/apk/res/android}versionCode"].encode('utf-8')
+ a = xml.attrib["{http://schemas.android.com/apk/res/android}versionCode"]
if string_is_integer(a):
vercode = a
except Exception:
if '/' in name:
name, subdir = name.split('/', 1)
- if name not in metadata.srclibs:
+ if name not in fdroidserver.metadata.srclibs:
raise VCSException('srclib ' + name + ' not found.')
- srclib = metadata.srclibs[name]
+ srclib = fdroidserver.metadata.srclibs[name]
sdir = os.path.join(srclib_dir, name)
def getpaths(build_dir, globpaths):
paths_map = getpaths_map(build_dir, globpaths)
paths = set()
- for k, v in paths_map.iteritems():
+ for k, v in paths_map.items():
for p in v:
paths.add(p)
return paths
self.path = os.path.join('stats', 'known_apks.txt')
self.apks = {}
if os.path.isfile(self.path):
- for line in file(self.path):
- t = line.rstrip().split(' ')
- if len(t) == 2:
- self.apks[t[0]] = (t[1], None)
- else:
- self.apks[t[0]] = (t[1], time.strptime(t[2], '%Y-%m-%d'))
+ with open(self.path, 'r') as f:
+ for line in f:
+ t = line.rstrip().split(' ')
+ if len(t) == 2:
+ self.apks[t[0]] = (t[1], None)
+ else:
+ self.apks[t[0]] = (t[1], time.strptime(t[2], '%Y-%m-%d'))
self.changed = False
def writeifchanged(self):
os.mkdir('stats')
lst = []
- for apk, app in self.apks.iteritems():
+ for apk, app in self.apks.items():
appid, added = app
line = apk + ' ' + appid
if added:
# with the most recent first.
def getlatest(self, num):
apps = {}
- for apk, app in self.apks.iteritems():
+ for apk, app in self.apks.items():
appid, added = app
if added:
if appid in apps:
apps[appid] = added
else:
apps[appid] = added
- sortedapps = sorted(apps.iteritems(), key=operator.itemgetter(1))[-num:]
+ sortedapps = sorted(apps.items(), key=operator.itemgetter(1))[-num:]
lst = [app for app, _ in sortedapps]
lst.reverse()
return lst
class PopenResult:
- returncode = None
- output = ''
+ def __init__(self):
+ self.returncode = None
+ self.output = None
def SdkToolsPopen(commands, cwd=None, output=True):
cwd=cwd, output=output)
-def FDroidPopen(commands, cwd=None, output=True, stderr_to_stdout=True):
+def FDroidPopenBytes(commands, cwd=None, output=True, stderr_to_stdout=True):
"""
- Run a command and capture the possibly huge output.
+ Run a command and capture the possibly huge output as bytes.
:param commands: command and argument list like in subprocess.Popen
:param cwd: optionally specifies a working directory
while not stderr_reader.eof():
while not stderr_queue.empty():
line = stderr_queue.get()
- sys.stderr.write(line)
+ sys.stderr.buffer.write(line)
sys.stderr.flush()
time.sleep(0.1)
stdout_queue = Queue()
stdout_reader = AsynchronousFileReader(p.stdout, stdout_queue)
+ buf = io.BytesIO()
# Check the queue for output (until there is no more to get)
while not stdout_reader.eof():
line = stdout_queue.get()
if output and options.verbose:
# Output directly to console
- sys.stderr.write(line)
+ sys.stderr.buffer.write(line)
sys.stderr.flush()
- result.output += line
+ buf.write(line)
time.sleep(0.1)
result.returncode = p.wait()
+ result.output = buf.getvalue()
+ buf.close()
+ return result
+
+
+def FDroidPopen(commands, cwd=None, output=True, stderr_to_stdout=True):
+ """
+ Run a command and capture the possibly huge output as a str.
+
+ :param commands: command and argument list like in subprocess.Popen
+ :param cwd: optionally specifies a working directory
+ :returns: A PopenResult.
+ """
+ result = FDroidPopenBytes(commands, cwd, output, stderr_to_stdout)
+ result.output = result.output.decode('utf-8')
return result
'''generate a random password for when generating keys'''
h = hashlib.sha256()
h.update(os.urandom(16)) # salt
- h.update(bytes(socket.getfqdn()))
- return h.digest().encode('base64').strip()
+ h.update(socket.getfqdn().encode('utf-8'))
+ passwd = base64.b64encode(h.digest()).strip()
+ return passwd.decode('utf-8')
def genkeystore(localconfig):
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# gpgsign.py - part of the FDroid server tools
# Copyright (C) 2014, Ciaran Gultnieks, ciaran@ciarang.com
from argparse import ArgumentParser
import logging
-import common
-from common import FDroidPopen
+from . import common
+from .common import FDroidPopen
config = None
options = None
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# import.py - part of the FDroid server tools
# Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
import sys
import os
import shutil
-import urllib
+import urllib.request
from argparse import ArgumentParser
-from ConfigParser import ConfigParser
+from configparser import ConfigParser
import logging
-import common
-import metadata
+
+from . import common
+from . import metadata
# Get the repo type and address from the given web page. The page is scanned
# Returns repotype, address, or None, reason
def getrepofrompage(url):
- req = urllib.urlopen(url)
+ req = urllib.request.urlopen(url)
if req.getcode() != 200:
return (None, 'Unable to get ' + url + ' - return code ' + str(req.getcode()))
page = req.read()
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# update.py - part of the FDroid server tools
# Copyright (C) 2010-2013, Ciaran Gultnieks, ciaran@ciarang.com
from argparse import ArgumentParser
import logging
-import common
+from . import common
config = {}
options = None
default_sdk_path = '/opt/android-sdk'
while not options.no_prompt:
try:
- s = raw_input('Enter the path to the Android SDK ('
- + default_sdk_path + ') here:\n> ')
+ s = input('Enter the path to the Android SDK ('
+ + default_sdk_path + ') here:\n> ')
except KeyboardInterrupt:
print('')
sys.exit(1)
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# install.py - part of the FDroid server tools
# Copyright (C) 2013, Ciaran Gultnieks, ciaran@ciarang.com
from argparse import ArgumentParser
import logging
-import common
-from common import SdkToolsPopen, FDroidException
+from . import common
+from .common import SdkToolsPopen, FDroidException
options = None
config = None
continue
apks[appid] = apkfile
- for appid, apk in apks.iteritems():
+ for appid, apk in apks.items():
if not apk:
raise FDroidException("No signed apk available for %s" % appid)
apks = {common.apknameinfo(apkfile)[0]: apkfile for apkfile in
sorted(glob.glob(os.path.join(output_dir, '*.apk')))}
- for appid, apk in apks.iteritems():
+ for appid, apk in apks.items():
# Get device list each time to avoid device not found errors
devs = devices()
if not devs:
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# lint.py - part of the FDroid server tool
# Copyright (C) 2013-2014 Daniel Martí <mvdan@mvdan.cc>
from argparse import ArgumentParser
import re
import sys
-from sets import Set
-import common
-import metadata
-import rewritemeta
+from . import common
+from . import metadata
+from . import rewritemeta
config = None
options = None
def check_regexes(app):
- for f, checks in regex_checks.iteritems():
+ for f, checks in regex_checks.items():
for m, r in checks:
v = app.get_field(f)
t = metadata.fieldtype(f)
if not app.Categories:
yield "Categories are not set"
-all_categories = Set([
+all_categories = set([
"Connectivity",
"Development",
"Games",
allapps = metadata.read_metadata(xref=True)
apps = common.read_app_args(options.appid, allapps, False)
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
if app.Disabled:
continue
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# metadata.py - part of the FDroid server tools
# Copyright (C) 2013, Ciaran Gultnieks, ciaran@ciarang.com
import glob
import cgi
import textwrap
-
-try:
- from cStringIO import StringIO
-except:
- from StringIO import StringIO
+import io
import yaml
# use libyaml if it is available
# use the C implementation when available
import xml.etree.cElementTree as ElementTree
-import common
+import fdroidserver.common
srclibs = None
# names. Should only be used for tests.
def field_dict(self):
d = {}
- for k, v in self.__dict__.iteritems():
+ for k, v in self.__dict__.items():
if k == 'builds':
d['builds'] = []
for build in v:
- b = {k: v for k, v in build.__dict__.iteritems() if not k.startswith('_')}
+ b = {k: v for k, v in build.__dict__.items() if not k.startswith('_')}
d['builds'].append(b)
elif not k.startswith('_'):
f = App.attr_to_field(k)
# Like dict.update(), but using human-readable field names
def update_fields(self, d):
- for f, v in d.iteritems():
+ for f, v in d.items():
if f == 'builds':
for b in v:
build = Build()
version = self.ndk
if not version:
version = 'r10e' # falls back to latest
- paths = common.config['ndk_paths']
+ paths = fdroidserver.common.config['ndk_paths']
if version not in paths:
return ''
return paths[version]
def update_flags(self, d):
- for f, v in d.iteritems():
+ for f, v in d.items():
self.set_flag(f, v)
flagtypes = {
self.laststate = self.stNONE
self.text_html = ''
self.text_txt = ''
- self.html = StringIO()
- self.text = StringIO()
+ self.html = io.StringIO()
+ self.text = io.StringIO()
self.para_lines = []
self.linkResolver = None
self.linkResolver = linkres
self.state = self.stNONE
whole_para = ' '.join(self.para_lines)
self.addtext(whole_para)
- wrapped = textwrap.fill(whole_para.decode('utf-8'), 80,
+ wrapped = textwrap.fill(whole_para, 80,
break_long_words=False,
break_on_hyphens=False)
- self.text.write(wrapped.encode('utf-8'))
+ self.text.write(wrapped)
self.html.write('</p>')
del self.para_lines[:]
if not os.path.exists(metadatapath):
return thisinfo
- metafile = open(metadatapath, "r")
+ metafile = open(metadatapath, "r", encoding='utf-8')
n = 0
for line in metafile:
return ("fdroid.app:" + appid, "Dummy name - don't know yet")
raise MetaDataException("Cannot resolve app id " + appid)
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
try:
description_html(app.Description, linkres)
except MetaDataException as e:
if metadatapath is None:
appid = None
else:
- appid, _ = common.get_extension(os.path.basename(metadatapath))
+ appid, _ = fdroidserver.common.get_extension(os.path.basename(metadatapath))
app = App()
app.metadatapath = metadatapath
# This function uses __dict__ to be faster
def post_metadata_parse(app):
- for k, v in app.__dict__.iteritems():
- if k not in app._modified:
- continue
+ for k in app._modified:
+ v = app.__dict__[k]
if type(v) in (float, int):
app.__dict__[k] = str(v)
for build in app.builds:
- for k, v in build.__dict__.iteritems():
-
- if k not in build._modified:
- continue
+ for k in build._modified:
+ v = build.__dict__[k]
if type(v) in (float, int):
build.__dict__[k] = str(v)
continue
build.__dict__[k] = re.sub(esc_newlines, '', v).lstrip().rstrip()
elif ftype == TYPE_BOOL:
# TODO handle this using <xsd:element type="xsd:boolean> in a schema
- if isinstance(v, basestring):
+ if isinstance(v, str):
build.__dict__[k] = _decode_bool(v)
elif ftype == TYPE_STRING:
if isinstance(v, bool) and v:
#
-def _decode_list(data):
- '''convert items in a list from unicode to basestring'''
- rv = []
- for item in data:
- if isinstance(item, unicode):
- item = item.encode('utf-8')
- elif isinstance(item, list):
- item = _decode_list(item)
- elif isinstance(item, dict):
- item = _decode_dict(item)
- rv.append(item)
- return rv
-
-
-def _decode_dict(data):
- '''convert items in a dict from unicode to basestring'''
- rv = {}
- for k, v in data.iteritems():
- if isinstance(k, unicode):
- k = k.encode('utf-8')
- if isinstance(v, unicode):
- v = v.encode('utf-8')
- elif isinstance(v, list):
- v = _decode_list(v)
- elif isinstance(v, dict):
- v = _decode_dict(v)
- rv[k] = v
- return rv
-
-
bool_true = re.compile(r'([Yy]es|[Tt]rue)')
bool_false = re.compile(r'([Nn]o|[Ff]alse)')
def parse_metadata(metadatapath):
- _, ext = common.get_extension(metadatapath)
- accepted = common.config['accepted_formats']
+ _, ext = fdroidserver.common.get_extension(metadatapath)
+ accepted = fdroidserver.common.config['accepted_formats']
if ext not in accepted:
raise MetaDataException('"%s" is not an accepted format, convert to: %s' % (
metadatapath, ', '.join(accepted)))
app = App()
app.metadatapath = metadatapath
- app.id, _ = common.get_extension(os.path.basename(metadatapath))
+ app.id, _ = fdroidserver.common.get_extension(os.path.basename(metadatapath))
- with open(metadatapath, 'r') as mf:
+ with open(metadatapath, 'r', encoding='utf-8') as mf:
if ext == 'txt':
parse_txt_metadata(mf, app)
elif ext == 'json':
def parse_json_metadata(mf, app):
- # fdroid metadata is only strings and booleans, no floats or ints. And
- # json returns unicode, and fdroidserver still uses plain python strings
+ # fdroid metadata is only strings and booleans, no floats or ints.
# TODO create schema using https://pypi.python.org/pypi/jsonschema
- jsoninfo = json.load(mf, object_hook=_decode_dict,
- parse_int=lambda s: s,
+ jsoninfo = json.load(mf, parse_int=lambda s: s,
parse_float=lambda s: s)
app.update_fields(jsoninfo)
for f in ['Description', 'Maintainer Notes']:
w_field_always('Binaries')
mf.write('\n')
- for build in sorted_builds(app.builds):
+ for build in app.builds:
if build.version == "Ignore":
continue
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# net.py - part of the FDroid server tools
# Copyright (C) 2015 Hans-Christoph Steiner <hans@eds.org>
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# publish.py - part of the FDroid server tools
# Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
import sys
import os
import shutil
-import md5
import glob
+import hashlib
from argparse import ArgumentParser
import logging
-import common
-import metadata
-from common import FDroidPopen, SdkToolsPopen, BuildException
+from . import common
+from . import metadata
+from .common import FDroidPopen, SdkToolsPopen, BuildException
config = None
options = None
vercodes = common.read_pkg_args(options.appid, True)
allaliases = []
for appid in allapps:
- m = md5.new()
- m.update(appid)
+ m = hashlib.md5()
+ m.update(appid.encode('utf-8'))
keyalias = m.hexdigest()[:8]
if keyalias in allaliases:
logging.error("There is a keyalias collision - publishing halted")
# For this particular app, the key alias is overridden...
keyalias = config['keyaliases'][appid]
if keyalias.startswith('@'):
- m = md5.new()
- m.update(keyalias[1:])
+ m = hashlib.md5()
+ m.update(keyalias[1:].encode('utf-8'))
keyalias = m.hexdigest()[:8]
else:
- m = md5.new()
- m.update(appid)
+ m = hashlib.md5()
+ m.update(appid.encode('utf-8'))
keyalias = m.hexdigest()[:8]
logging.info("Key alias: " + keyalias)
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# readmeta.py - part of the FDroid server tools
# Copyright (C) 2014 Daniel Martí <mvdan@mvdan.cc>
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from argparse import ArgumentParser
-import common
-import metadata
+from . import common
+from . import metadata
def main():
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# rewritemeta.py - part of the FDroid server tools
# This cleans up the original .txt metadata file format.
from argparse import ArgumentParser
import os
import logging
-try:
- from cStringIO import StringIO
-except:
- from StringIO import StringIO
+import io
-import common
-import metadata
+from . import common
+from . import metadata
config = None
options = None
def proper_format(app):
- s = StringIO()
+ s = io.StringIO()
# TODO: currently reading entire file again, should reuse first
# read in metadata.py
with open(app.metadatapath, 'r') as f:
if options.to is not None and options.to not in supported:
parser.error("Must give a valid format to --to")
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
base, ext = common.get_extension(app.metadatapath)
if not options.to and ext not in supported:
logging.info("Ignoring %s file at '%s'" % (ext, app.metadatapath))
if options.list:
if not proper_format(app):
- print app.metadatapath
+ print(app.metadatapath)
continue
with open(base + '.' + to_ext, 'w') as f:
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# scanner.py - part of the FDroid server tools
# Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
from argparse import ArgumentParser
import logging
-import common
-import metadata
-from common import BuildException, VCSException
+from . import common
+from . import metadata
+from .common import BuildException, VCSException
config = None
options = None
}
def suspects_found(s):
- for n, r in usual_suspects.iteritems():
+ for n, r in usual_suspects.items():
if r.match(s):
yield n
scandelete_worked = set()
def toignore(fd):
- for k, paths in scanignore.iteritems():
+ for k, paths in scanignore.items():
for p in paths:
if fd.startswith(p):
scanignore_worked.add(k)
return False
def todelete(fd):
- for k, paths in scandelete.iteritems():
+ for k, paths in scandelete.items():
for p in paths:
if fd.startswith(p):
scandelete_worked.add(k)
elif ext == 'java':
if not os.path.isfile(fp):
continue
- for line in file(fp):
- if 'DexClassLoader' in line:
- count += handleproblem('DexClassLoader', fd, fp)
- break
+ with open(fp, 'r') as f:
+ for line in f:
+ if 'DexClassLoader' in line:
+ count += handleproblem('DexClassLoader', fd, fp)
+ break
elif ext == 'gradle':
if not os.path.isfile(fp):
srclib_dir = os.path.join(build_dir, 'srclib')
extlib_dir = os.path.join(build_dir, 'extlib')
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
if app.Disabled:
logging.info("Skipping %s: disabled" % appid)
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# server.py - part of the FDroid server tools
# Copyright (C) 2010-15, Ciaran Gultnieks, ciaran@ciarang.com
import subprocess
from argparse import ArgumentParser
import logging
-import common
+
+from . import common
config = None
options = None
sftp = ssh.open_sftp()
if os.path.basename(remotepath) \
not in sftp.listdir(os.path.dirname(remotepath)):
- sftp.mkdir(remotepath, mode=0755)
+ sftp.mkdir(remotepath, mode=0o755)
for repo_section in repo_sections:
repo_path = os.path.join(remotepath, repo_section)
if os.path.basename(repo_path) \
not in sftp.listdir(remotepath):
- sftp.mkdir(repo_path, mode=0755)
+ sftp.mkdir(repo_path, mode=0o755)
sftp.close()
ssh.close()
elif options.command == 'update':
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# gpgsign.py - part of the FDroid server tools
# Copyright (C) 2015, Ciaran Gultnieks, ciaran@ciarang.com
from argparse import ArgumentParser
import logging
-import common
-from common import FDroidPopen
+from . import common
+from .common import FDroidPopen
config = None
options = None
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# stats.py - part of the FDroid server tools
# Copyright (C) 2010-13, Ciaran Gultnieks, ciaran@ciarang.com
import paramiko
import socket
import logging
-import common
-import metadata
import subprocess
from collections import Counter
+from . import common
+from . import metadata
+
def carbon_send(key, value):
s = socket.socket()
sys.exit(1)
# Get all metadata-defined apps...
- allmetaapps = [app for app in metadata.read_metadata().itervalues()]
+ allmetaapps = [app for app in metadata.read_metadata().values()]
metaapps = [app for app in allmetaapps if not app.Disabled]
statsdir = 'stats'
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# update.py - part of the FDroid server tools
# Copyright (C) 2010-2015, Ciaran Gultnieks, ciaran@ciarang.com
import zipfile
import hashlib
import pickle
-import urlparse
+import urllib.parse
from datetime import datetime, timedelta
from xml.dom.minidom import Document
from argparse import ArgumentParser
from pyasn1.error import PyAsn1Error
from pyasn1.codec.der import decoder, encoder
from pyasn1_modules import rfc2315
-from hashlib import md5
from binascii import hexlify, unhexlify
from PIL import Image
import logging
-import common
-import metadata
-from common import FDroidPopen, SdkToolsPopen
-from metadata import MetaDataException
+from . import common
+from . import metadata
+from .common import FDroidPopen, FDroidPopenBytes, SdkToolsPopen
+from .metadata import MetaDataException
screen_densities = ['640', '480', '320', '240', '160', '120']
:param apkcache: current apk cache information
:param repodirs: the repo directories to process
"""
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
for build in app.builds:
if not build.disable:
continue
cert_encoded = encoder.encode(certificates)[4:]
- return md5(cert_encoded.encode('hex')).hexdigest()
+ return hashlib.md5(hexlify(cert_encoded)).hexdigest()
def scan_apks(apps, apkcache, repodir, knownapks, use_date_from_apk=False):
def cert_fingerprint(data):
digest = hashlib.sha256(data).digest()
ret = []
- ret.append(' '.join("%02X" % ord(b) for b in digest))
+ ret.append(' '.join("%02X" % b for b in bytearray(digest)))
return " ".join(ret)
if 'repo_pubkey' in config:
pubkey = unhexlify(config['repo_pubkey'])
else:
- p = FDroidPopen([config['keytool'], '-exportcert',
- '-alias', config['repo_keyalias'],
- '-keystore', config['keystore'],
- '-storepass:file', config['keystorepassfile']]
- + config['smartcardoptions'],
- output=False, stderr_to_stdout=False)
+ p = FDroidPopenBytes([config['keytool'], '-exportcert',
+ '-alias', config['repo_keyalias'],
+ '-keystore', config['keystore'],
+ '-storepass:file', config['keystorepassfile']]
+ + config['smartcardoptions'],
+ output=False, stderr_to_stdout=False)
if p.returncode != 0 or len(p.output) < 20:
msg = "Failed to get repo pubkey!"
if config['keystore'] == 'NONE':
mirrorcheckfailed = False
for mirror in config.get('mirrors', []):
- base = os.path.basename(urlparse.urlparse(mirror).path.rstrip('/'))
+ base = os.path.basename(urllib.parse.urlparse(mirror).path.rstrip('/'))
if config.get('nonstandardwebroot') is not True and base != 'fdroid':
logging.error("mirror '" + mirror + "' does not end with 'fdroid'!")
mirrorcheckfailed = True
repoel.setAttribute("icon", os.path.basename(config['archive_icon']))
repoel.setAttribute("url", config['archive_url'])
addElement('description', config['archive_description'], doc, repoel)
- urlbasepath = os.path.basename(urlparse.urlparse(config['archive_url']).path)
+ urlbasepath = os.path.basename(urllib.parse.urlparse(config['archive_url']).path)
for mirror in config.get('mirrors', []):
- addElement('mirror', urlparse.urljoin(mirror, urlbasepath), doc, repoel)
+ addElement('mirror', urllib.parse.urljoin(mirror, urlbasepath), doc, repoel)
else:
repoel.setAttribute("name", config['repo_name'])
repoel.setAttribute("icon", os.path.basename(config['repo_icon']))
repoel.setAttribute("url", config['repo_url'])
addElement('description', config['repo_description'], doc, repoel)
- urlbasepath = os.path.basename(urlparse.urlparse(config['repo_url']).path)
+ urlbasepath = os.path.basename(urllib.parse.urlparse(config['repo_url']).path)
for mirror in config.get('mirrors', []):
- addElement('mirror', urlparse.urljoin(mirror, urlbasepath), doc, repoel)
+ addElement('mirror', urllib.parse.urljoin(mirror, urlbasepath), doc, repoel)
repoel.setAttribute("version", "15")
repoel.setAttribute("timestamp", str(int(time.time())))
logging.warning("\tfdroid update --create-key")
sys.exit(1)
- repoel.setAttribute("pubkey", extract_pubkey())
+ repoel.setAttribute("pubkey", extract_pubkey().decode('utf-8'))
root.appendChild(repoel)
for appid in sortedids:
os.symlink(sigfile_path, siglinkname)
if options.pretty:
- output = doc.toprettyxml()
+ output = doc.toprettyxml(encoding='utf-8')
else:
- output = doc.toxml()
+ output = doc.toxml(encoding='utf-8')
with open(os.path.join(repodir, 'index.xml'), 'wb') as f:
f.write(output)
def archive_old_apks(apps, apks, archapks, repodir, archivedir, defaultkeepversions):
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
if app.ArchivePolicy:
keepversions = int(app.ArchivePolicy[:-9])
# Generate a list of categories...
categories = set()
- for app in apps.itervalues():
+ for app in apps.values():
categories.update(app.Categories)
# Read known apks data (will be updated and written back when we've finished)
# level. When doing this, we use the info from the most recent version's apk.
# We deal with figuring out when the app was added and last updated at the
# same time.
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
bestver = 0
for apk in apks + archapks:
if apk['id'] == appid:
# Sort the app list by name, then the web site doesn't have to by default.
# (we had to wait until we'd scanned the apks to do this, because mostly the
# name comes from there!)
- sortedids = sorted(apps.iterkeys(), key=lambda appid: apps[appid].Name.upper())
+ sortedids = sorted(apps.keys(), key=lambda appid: apps[appid].Name.upper())
# APKs are placed into multiple repos based on the app package, providing
# per-app subscription feeds for nightly builds and things like it
if config['per_app_repos']:
add_apks_to_per_app_repos(repodirs[0], apks)
- for appid, app in apps.iteritems():
+ for appid, app in apps.items():
repodir = os.path.join(appid, 'fdroid', 'repo')
appdict = dict()
appdict[appid] = app
# Generate latest apps data for widget
if os.path.exists(os.path.join('stats', 'latestapps.txt')):
data = ''
- for line in file(os.path.join('stats', 'latestapps.txt')):
- appid = line.rstrip()
- data += appid + "\t"
- app = apps[appid]
- data += app.Name + "\t"
- if app.icon is not None:
- data += app.icon + "\t"
- data += app.License + "\n"
+ with open(os.path.join('stats', 'latestapps.txt'), 'r') as f:
+ for line in f:
+ appid = line.rstrip()
+ data += appid + "\t"
+ app = apps[appid]
+ data += app.Name + "\t"
+ if app.icon is not None:
+ data += app.icon + "\t"
+ data += app.License + "\n"
with open(os.path.join(repodirs[0], 'latestapps.dat'), 'w') as f:
f.write(data)
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
#
# verify.py - part of the FDroid server tools
# Copyright (C) 2013, Ciaran Gultnieks, ciaran@ciarang.com
from argparse import ArgumentParser
import logging
-import common
-import net
-from common import FDroidException
+from . import common
+from . import net
+from .common import FDroidException
options = None
config = None
done
fi
-# In the default configuration, the checks E123, E133, E226, E241 and E242 are
-# ignored because they are not rules unanimously accepted
-# On top of those, we ignore:
+# We ignore the following PEP8 warnings
+# * E123: closing bracket does not match indentation of opening bracket's line
+# - Broken if multiple indentation levels start on a single line
# * E501: line too long (82 > 79 characters)
# - Recommended for readability but not enforced
# - Some lines are awkward to wrap around a char limit
# * W503: line break before binary operator
-# - It's quite new
# - Quite pedantic
-PEP8_IGNORE="E123,E133,E226,E241,E242,E501,W503"
+PEP8_IGNORE="E123,E501,W503"
err() {
echo ERROR: "$@"
command -v $1 1>/dev/null
}
-if cmd_exists pyflakes-python2; then
- PYFLAKES=pyflakes-python2
-elif cmd_exists pyflakes; then
- PYFLAKES=pyflakes
-else
- PYFLAKES=echo
- warn "pyflakes is not installed, using dummy placeholder!"
-fi
+find_command() {
+ local name=$1
+ for suff in "3" "-python3" ""; do
+ cmd=${1}${suff}
+ if cmd_exists $cmd; then
+ echo -n $cmd
+ return 0
+ fi
+ done
+ warn "$1 is not installed, using dummy placeholder!"
+ echo -n echo
+}
-if cmd_exists pep8-python2; then
- PEP8=pep8-python2
-elif cmd_exists pep8; then
- PEP8=pep8
-else
- err "pep8 is not installed!"
-fi
+PYFLAKES=$(find_command pyflakes)
+PEP8=$(find_command pep8)
if [ "$PY_FILES $PY_TEST_FILES" != " " ]; then
if ! $PYFLAKES $PY_FILES $PY_TEST_FILES; then
-#!/usr/bin/env python2
+#!/usr/bin/env python3
import os
import sys
line = p.stdout.readline()
if len(line) == 0:
break
- print line,
+ print(line)
out += line
p.wait()
else:
# load config file, if present
if os.path.exists('makebuildserver.config.py'):
- execfile('makebuildserver.config.py', config)
+ exec(compile(open('makebuildserver.config.py').read(), 'makebuildserver.config.py', 'exec'), config)
elif os.path.exists('makebs.config.py'):
# this is the old name for the config file
- execfile('makebs.config.py', config)
+ exec(compile(open('makebs.config.py').read(), 'makebs.config.py', 'exec'), config)
if not os.path.exists('makebuildserver') or not os.path.exists(serverdir):
- print 'This must be run from the correct directory!'
+ print('This must be run from the correct directory!')
sys.exit(1)
if os.path.exists(boxfile):
# Update cached files.
cachedir = config['cachedir']
if not os.path.exists(cachedir):
- os.makedirs(cachedir, 0755)
+ os.makedirs(cachedir, 0o755)
cachefiles = [
('android-sdk_r24.4.1-linux.tgz',
if os.path.exists(relpath) and os.stat(relpath).st_size == 0:
os.remove(relpath)
if not os.path.exists(relpath):
- print "Downloading " + f + " to cache"
+ print("Downloading " + f + " to cache")
if subprocess.call(['wget', src, '-O', f], cwd=cachedir) != 0:
- print "...download of " + f + " failed."
+ print("...download of " + f + " failed.")
sys.exit(1)
if shasum:
v = sha256_for_file(relpath)
if v != shasum:
- print "Invalid shasum of '" + v + "' detected for " + f
+ print("Invalid shasum of '" + v + "' detected for " + f)
sys.exit(1)
else:
- print "...shasum verified for " + f
+ print("...shasum verified for " + f)
wanted.append(f)
with open(vf, 'r') as f:
oldvf = f.read()
if oldvf != vagrantfile:
- print "Server configuration has changed, rebuild from scratch is required"
+ print("Server configuration has changed, rebuild from scratch is required")
vagrant(['destroy', '-f'], serverdir)
else:
- print "Re-provisioning existing server"
+ print("Re-provisioning existing server")
writevf = False
else:
- print "No existing server - building from scratch"
+ print("No existing server - building from scratch")
if writevf:
with open(vf, 'w') as f:
f.write(vagrantfile)
-print "Configuring build server VM"
+print("Configuring build server VM")
returncode, out = vagrant(['up', '--provision'], serverdir, printout=True)
with open(os.path.join(serverdir, 'up.log'), 'w') as log:
log.write(out)
if returncode != 0:
- print "Failed to configure server"
+ print("Failed to configure server")
sys.exit(1)
-print "Writing buildserver ID"
+print("Writing buildserver ID")
p = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
buildserverid = p.communicate()[0].strip()
-print "...ID is " + buildserverid
+print("...ID is " + buildserverid)
subprocess.call(
['vagrant', 'ssh', '-c', 'sh -c "echo {0} >/home/vagrant/buildserverid"'
.format(buildserverid)],
cwd=serverdir)
-print "Stopping build server VM"
+print("Stopping build server VM")
vagrant(['halt'], serverdir)
-print "Waiting for build server VM to be finished"
+print("Waiting for build server VM to be finished")
ready = False
while not ready:
time.sleep(2)
returncode, out = vagrant(['status'], serverdir)
if returncode != 0:
- print "Error while checking status"
+ print("Error while checking status")
sys.exit(1)
for line in out.splitlines():
if line.startswith("default"):
if line.find("poweroff") != -1:
ready = True
else:
- print "Status: " + line
+ print("Status: " + line)
-print "Packaging"
+print("Packaging")
vagrant(['package', '--output', os.path.join('..', boxfile)], serverdir,
printout=options.verbose)
-print "Adding box"
+print("Adding box")
vagrant(['box', 'add', 'buildserver', boxfile, '-f'],
printout=options.verbose)
-#!/usr/bin/env python2
+#!/usr/bin/env python3
from setuptools import setup
import sys
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
break
return True
else:
- print 'no build-tools found: ' + build_tools
+ print('no build-tools found: ' + build_tools)
return False
def _find_all(self):
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
break
return True
else:
- print 'no build-tools found: ' + build_tools
+ print('no build-tools found: ' + build_tools)
return False
def _find_all(self):
if self._set_build_tools():
self._find_all()
else:
- print 'no build-tools found: ' + build_tools
+ print('no build-tools found: ' + build_tools)
def testIsApkDebuggable(self):
config = dict()
#------------------------------------------------------------------------------#
# test building the source tarball, then installing it
cd $WORKSPACE
-python2 setup.py sdist
+python3 setup.py sdist
rm -rf $WORKSPACE/env
-virtualenv --python=python2 $WORKSPACE/env
+virtualenv --python=python3 $WORKSPACE/env
. $WORKSPACE/env/bin/activate
pip install dist/fdroidserver-*.tar.gz
# test install using install direct from git repo
cd $WORKSPACE
rm -rf $WORKSPACE/env
-virtualenv --python=python2 --system-site-packages $WORKSPACE/env
+virtualenv --python=python3 --system-site-packages $WORKSPACE/env
. $WORKSPACE/env/bin/activate
pip install -e $WORKSPACE
-python2 setup.py install
+python3 setup.py install
# run tests in new pip+virtualenv install
fdroid=$WORKSPACE/env/bin/fdroid $WORKSPACE/tests/run-tests $apksource
cd $WORKSPACE
set +e
# use the virtualenv python so pylint checks against its installed libs
- PYTHONPATH=$WORKSPACE/.pylint-plugins python2 /usr/bin/pylint \
+ PYTHONPATH=$WORKSPACE/.pylint-plugins python3 /usr/bin/pylint \
--output-format=parseable --reports=n \
--load-plugins astng_hashlib \
fdroidserver/*.py fdroid makebuildserver setup.py > $WORKSPACE/pylint.parseable
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
import os
import sys
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
devices = fdroidserver.install.devices()
self.assertIsInstance(devices, list, 'install.devices() did not return a list!')
for device in devices:
- self.assertIsInstance(device, basestring)
+ self.assertIsInstance(device, str)
if __name__ == "__main__":
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
savepath = os.path.join('metadata', appid + '.pickle')
frommeta = app.field_dict()
self.assertTrue(appid in apps)
- with open(savepath, 'r') as f:
+ with open(savepath, 'rb') as f:
frompickle = pickle.load(f)
self.assertEquals(frommeta, frompickle)
# Uncomment to overwrite
# allow the location of python to be overridden
if [ -z $python ]; then
- python=python2
+ python=python3
fi
set -x # show each command as it is executed
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
+#!/usr/bin/env python3
# http://www.drdobbs.com/testing/unit-testing-with-python/240165163
import os
import sys
import unittest
+from binascii import unhexlify
localmodule = os.path.realpath(
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
self.assertEquals(len(sig), len(pysig),
"the length of the two sigs are different!")
try:
- self.assertEquals(sig.decode('hex'), pysig.decode('hex'),
+ self.assertEquals(unhexlify(sig), unhexlify(pysig),
"the length of the two sigs are different!")
except TypeError as e:
- print e
+ print(e)
self.assertTrue(False, 'TypeError!')
def testBadGetsig(self):