Support (and require) Python 3

We need to be able to support Python 3 so that we can parse master of
OE-Core with bitbake (which now requires it). This now means the
interface itself and the update script require Python 3.4+.

Part of the implementation for [YOCTO #9704].

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
This commit is contained in:
Paul Eggleton 2016-06-03 23:11:49 +12:00
parent 2227102973
commit 29c6458dca
10 changed files with 36 additions and 32 deletions

6
README
View File

@ -11,6 +11,7 @@ Setup
In order to make use of this application you will need: In order to make use of this application you will need:
* Python 3.4+
* Django 1.6.x - tested with 1.6.10; newer versions may work, but * Django 1.6.x - tested with 1.6.10; newer versions may work, but
the application has not been tested with 1.7 or newer. the application has not been tested with 1.7 or newer.
* For production usage, a web server set up to host Django applications * For production usage, a web server set up to host Django applications
@ -32,8 +33,9 @@ In order to make use of this application you will need:
have to have Django installed, have the same or similar configuration have to have Django installed, have the same or similar configuration
in settings.py and have access to the database used by the web in settings.py and have access to the database used by the web
application): application):
* Python 2.7.3 * Python 2.7.6+ / Python 3.4+ to match with the version of BitBake
* GitPython (python-git) version 0.3.1 or later for the OpenEmbedded branch being parsed
* GitPython (python-git) version 2.0 or later
Setup instructions: Setup instructions:

View File

@ -50,7 +50,7 @@ def generate_patches(tinfoil, fetchdir, changeset, outputdir):
outfile = open(os.path.join(tmpoutdir, patchname), 'w') outfile = open(os.path.join(tmpoutdir, patchname), 'w')
last_layer = layer last_layer = layer
recipefile = str(os.path.join(layerfetchdir, layerbranch.vcs_subdir, change.recipe.filepath, change.recipe.filename)) recipefile = str(os.path.join(layerfetchdir, layerbranch.vcs_subdir, change.recipe.filepath, change.recipe.filename))
varlist = list(set(fields.keys() + meta_vars)) varlist = list(set(list(fields.keys()) + meta_vars))
varfiles = recipeparse.get_var_files(recipefile, varlist, config_data_copy) varfiles = recipeparse.get_var_files(recipefile, varlist, config_data_copy)
filevars = localise_file_vars(recipefile, varfiles, fields.keys()) filevars = localise_file_vars(recipefile, varfiles, fields.keys())
for f, fvars in filevars.items(): for f, fvars in filevars.items():
@ -66,7 +66,7 @@ def generate_patches(tinfoil, fetchdir, changeset, outputdir):
ret = None ret = None
if len(patches) > 1: if len(patches) > 1:
(tmptarfd, tmptarname) = tempfile.mkstemp('.tar.gz', 'bulkchange-', outputdir) (tmptarfd, tmptarname) = tempfile.mkstemp('.tar.gz', 'bulkchange-', outputdir)
tmptarfile = os.fdopen(tmptarfd, "w") tmptarfile = os.fdopen(tmptarfd, "wb")
tar = tarfile.open(None, "w:gz", tmptarfile) tar = tarfile.open(None, "w:gz", tmptarfile)
for patch in patches: for patch in patches:
patchfn = os.path.join(tmpoutdir, patch) patchfn = os.path.join(tmpoutdir, patch)
@ -75,7 +75,7 @@ def generate_patches(tinfoil, fetchdir, changeset, outputdir):
ret = tmptarname ret = tmptarname
elif len(patches) == 1: elif len(patches) == 1:
(tmppatchfd, tmppatchname) = tempfile.mkstemp('.patch', 'bulkchange-', outputdir) (tmppatchfd, tmppatchname) = tempfile.mkstemp('.patch', 'bulkchange-', outputdir)
tmppatchfile = os.fdopen(tmppatchfd, "w") tmppatchfile = os.fdopen(tmppatchfd, "wb")
with open(os.path.join(tmpoutdir, patches[0]), "rb") as patchfile: with open(os.path.join(tmpoutdir, patches[0]), "rb") as patchfile:
shutil.copyfileobj(patchfile, tmppatchfile) shutil.copyfileobj(patchfile, tmppatchfile)
tmppatchfile.close() tmppatchfile.close()
@ -93,7 +93,7 @@ def patch_recipe(fn, relpath, values):
remainingnames = {} remainingnames = {}
for k in values.keys(): for k in values.keys():
remainingnames[k] = recipe_progression.index(k) if k in recipe_progression else -1 remainingnames[k] = recipe_progression.index(k) if k in recipe_progression else -1
remainingnames = SortedDict(sorted(remainingnames.iteritems(), key=lambda x: x[1])) remainingnames = SortedDict(sorted(remainingnames.items(), key=lambda x: x[1]))
with tempfile.NamedTemporaryFile('w', delete=False) as tf: with tempfile.NamedTemporaryFile('w', delete=False) as tf:
def outputvalue(name): def outputvalue(name):
@ -234,7 +234,7 @@ def main():
utils.unlock_file(lockfile) utils.unlock_file(lockfile)
if outp: if outp:
print outp print(outp)
else: else:
sys.stderr.write("No changes to write\n") sys.stderr.write("No changes to write\n")
sys.exit(1) sys.exit(1)

View File

@ -26,7 +26,7 @@ class Branch(models.Model):
class Meta: class Meta:
verbose_name_plural = "Branches" verbose_name_plural = "Branches"
def __unicode__(self): def __str__(self):
return self.name return self.name
@ -106,7 +106,7 @@ class LayerItem(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('layer_item', args=('master',self.name)); return reverse('layer_item', args=('master',self.name));
def __unicode__(self): def __str__(self):
return self.name return self.name
@ -188,7 +188,7 @@ class LayerBranch(models.Model):
url = resolveComponents(url) url = resolveComponents(url)
return url return url
def __unicode__(self): def __str__(self):
return "%s: %s" % (self.layer.name, self.branch.name) return "%s: %s" % (self.layer.name, self.branch.name)
@ -203,7 +203,7 @@ class LayerMaintainer(models.Model):
responsibility = models.CharField(max_length=200, blank=True, help_text='Specific area(s) this maintainer is responsible for, if not the entire layer') responsibility = models.CharField(max_length=200, blank=True, help_text='Specific area(s) this maintainer is responsible for, if not the entire layer')
status = models.CharField(max_length=1, choices=MAINTAINER_STATUS_CHOICES, default='A') status = models.CharField(max_length=1, choices=MAINTAINER_STATUS_CHOICES, default='A')
def __unicode__(self): def __str__(self):
respstr = "" respstr = ""
if self.responsibility: if self.responsibility:
respstr = " (%s)" % self.responsibility respstr = " (%s)" % self.responsibility
@ -217,7 +217,7 @@ class LayerDependency(models.Model):
class Meta: class Meta:
verbose_name_plural = "Layer dependencies" verbose_name_plural = "Layer dependencies"
def __unicode__(self): def __str__(self):
return "%s depends on %s" % (self.layerbranch.layer.name, self.dependency.name) return "%s depends on %s" % (self.layerbranch.layer.name, self.dependency.name)
@ -225,7 +225,7 @@ class LayerNote(models.Model):
layer = models.ForeignKey(LayerItem) layer = models.ForeignKey(LayerItem)
text = models.TextField() text = models.TextField()
def __unicode__(self): def __str__(self):
return "%s: %s" % (self.layer.name, self.text) return "%s: %s" % (self.layer.name, self.text)
@ -266,7 +266,7 @@ class Recipe(models.Model):
else: else:
return self.filename.split('_')[0] return self.filename.split('_')[0]
def __unicode__(self): def __str__(self):
return os.path.join(self.filepath, self.filename) return os.path.join(self.filepath, self.filename)
@ -278,7 +278,7 @@ class RecipeFileDependency(models.Model):
class Meta: class Meta:
verbose_name_plural = "Recipe file dependencies" verbose_name_plural = "Recipe file dependencies"
def __unicode__(self): def __str__(self):
return '%s' % self.path return '%s' % self.path
@ -341,7 +341,7 @@ class Machine(models.Model):
url = self.layerbranch.file_url(os.path.join('conf/machine/%s.conf' % self.name)) url = self.layerbranch.file_url(os.path.join('conf/machine/%s.conf' % self.name))
return url or '' return url or ''
def __unicode__(self): def __str__(self):
return '%s (%s)' % (self.name, self.layerbranch.layer.name) return '%s (%s)' % (self.name, self.layerbranch.layer.name)
@ -367,7 +367,7 @@ class BBAppend(models.Model):
return fnmatch.fnmatch(recipename, appendname.replace('%', '*')) return fnmatch.fnmatch(recipename, appendname.replace('%', '*'))
return False return False
def __unicode__(self): def __str__(self):
return os.path.join(self.filepath, self.filename) return os.path.join(self.filepath, self.filename)
@ -383,7 +383,7 @@ class BBClass(models.Model):
url = self.layerbranch.file_url(os.path.join('classes', "%s.bbclass" % self.name)) url = self.layerbranch.file_url(os.path.join('classes', "%s.bbclass" % self.name))
return url or '' return url or ''
def __unicode__(self): def __str__(self):
return '%s (%s)' % (self.name, self.layerbranch.layer.name) return '%s (%s)' % (self.name, self.layerbranch.layer.name)
@ -391,7 +391,7 @@ class RecipeChangeset(models.Model):
user = models.ForeignKey(User) user = models.ForeignKey(User)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
def __unicode__(self): def __str__(self):
return '%s' % (self.name) return '%s' % (self.name)

View File

@ -82,10 +82,10 @@ def main():
layerrecipes = Recipe.objects.filter(layer=layer) layerrecipes = Recipe.objects.filter(layer=layer)
for recipe in layerrecipes: for recipe in layerrecipes:
fullpath = str(os.path.join(repodir, layer.vcs_subdir, recipe.filepath, recipe.filename)) fullpath = str(os.path.join(repodir, layer.vcs_subdir, recipe.filepath, recipe.filename))
print fullpath print(fullpath)
try: try:
envdata = bb.cache.Cache.loadDataFull(fullpath, [], tinfoil.config_data) envdata = bb.cache.Cache.loadDataFull(fullpath, [], tinfoil.config_data)
print "DESCRIPTION = \"%s\"" % envdata.getVar("DESCRIPTION", True) print("DESCRIPTION = \"%s\"" % envdata.getVar("DESCRIPTION", True))
except Exception as e: except Exception as e:
logger.info("Unable to read %s: %s", fullpath, str(e)) logger.info("Unable to read %s: %s", fullpath, str(e))

View File

@ -23,7 +23,7 @@ def addurlparameter(parser, token):
from re import split from re import split
bits = split(r'\s+', token.contents, 2) bits = split(r'\s+', token.contents, 2)
if len(bits) < 2: if len(bits) < 2:
raise TemplateSyntaxError, "'%s' tag requires two arguments" % bits[0] raise TemplateSyntaxError("'%s' tag requires two arguments" % bits[0])
return AddParameter(bits[1],bits[2]) return AddParameter(bits[1],bits[2])
register.tag('addurlparameter', addurlparameter) register.tag('addurlparameter', addurlparameter)

View File

@ -164,7 +164,7 @@ def get_github_layerinfo(layer_url, username = None, password = None):
data = resp.read() data = resp.read()
json_data = json.loads(data) json_data = json.loads(data)
#headers = dict((key, value) for key, value in resp.getheaders()) #headers = dict((key, value) for key, value in resp.getheaders())
#print headers #print(headers)
owner_resp = github_api_call(json_data['owner']['url'].split('api.github.com')[-1]) owner_resp = github_api_call(json_data['owner']['url'].split('api.github.com')[-1])
if resp.status in [200, 302]: if resp.status in [200, 302]:
owner_data = owner_resp.read() owner_data = owner_resp.read()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# Fetch layer repositories and update layer index database # Fetch layer repositories and update layer index database
# #

View File

@ -11,7 +11,7 @@ from django.core.urlresolvers import reverse_lazy
from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDetailView, RecipeSearchView, MachineSearchView, PlainTextListView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, HistoryListView, EditProfileFormView, AdvancedRecipeSearchView, BulkChangeView, BulkChangeSearchView, bulk_change_edit_view, bulk_change_patch_view, BulkChangeDeleteView, RecipeDetailView, RedirectParamsView, ClassicRecipeSearchView, ClassicRecipeDetailView, ClassicRecipeStatsView from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDetailView, RecipeSearchView, MachineSearchView, PlainTextListView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, HistoryListView, EditProfileFormView, AdvancedRecipeSearchView, BulkChangeView, BulkChangeSearchView, bulk_change_edit_view, bulk_change_patch_view, BulkChangeDeleteView, RecipeDetailView, RedirectParamsView, ClassicRecipeSearchView, ClassicRecipeDetailView, ClassicRecipeStatsView
from layerindex.models import LayerItem, Recipe, RecipeChangeset from layerindex.models import LayerItem, Recipe, RecipeChangeset
from rest_framework import routers from rest_framework import routers
import restviews from . import restviews
from django.conf.urls import include from django.conf.urls import include
router = routers.DefaultRouter() router = routers.DefaultRouter()

View File

@ -6,7 +6,8 @@
# Licensed under the MIT license, see COPYING.MIT for details # Licensed under the MIT license, see COPYING.MIT for details
import sys import sys
import os.path import os
import tempfile
import subprocess import subprocess
import logging import logging
import time import time
@ -32,10 +33,10 @@ def runcmd(cmd, destdir=None, printerr=True, logger=None):
return output if succeed return output if succeed
""" """
#logger.debug("run cmd '%s' in %s" % (cmd, os.getcwd() if destdir is None else destdir)) #logger.debug("run cmd '%s' in %s" % (cmd, os.getcwd() if destdir is None else destdir))
out = os.tmpfile() out = tempfile.TemporaryFile()
try: try:
subprocess.check_call(cmd, stdout=out, stderr=out, cwd=destdir, shell=True) subprocess.check_call(cmd, stdout=out, stderr=out, cwd=destdir, shell=True)
except subprocess.CalledProcessError,e: except subprocess.CalledProcessError as e:
out.seek(0) out.seek(0)
if printerr: if printerr:
output = out.read() output = out.read()

View File

@ -4,6 +4,7 @@
# #
# Licensed under the MIT license, see COPYING.MIT for details # Licensed under the MIT license, see COPYING.MIT for details
import sys
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404 from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
from django.core.urlresolvers import reverse, reverse_lazy, resolve from django.core.urlresolvers import reverse, reverse_lazy, resolve
@ -26,7 +27,7 @@ from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from reversion.models import Revision from reversion.models import Revision
import simplesearch from . import simplesearch
import settings import settings
from django.dispatch import receiver from django.dispatch import receiver
import reversion import reversion
@ -212,14 +213,14 @@ def bulk_change_edit_view(request, template_name, pk):
def bulk_change_patch_view(request, pk): def bulk_change_patch_view(request, pk):
import os import os
import os.path import os.path
import utils from layerindex.utils import runcmd
changeset = get_object_or_404(RecipeChangeset, pk=pk) changeset = get_object_or_404(RecipeChangeset, pk=pk)
# FIXME this couples the web server and machine running the update script together, # FIXME this couples the web server and machine running the update script together,
# but given that it's a separate script the way is open to decouple them in future # but given that it's a separate script the way is open to decouple them in future
try: try:
ret = utils.runcmd('python bulkchange.py %d %s' % (int(pk), settings.TEMP_BASE_DIR), os.path.dirname(__file__)) ret = runcmd('%s bulkchange.py %d %s' % (sys.executable, int(pk), settings.TEMP_BASE_DIR), os.path.dirname(__file__))
if ret: if ret:
fn = ret.splitlines()[-1] fn = ret.splitlines()[-1].decode('utf-8')
if os.path.exists(fn): if os.path.exists(fn):
if fn.endswith('.tar.gz'): if fn.endswith('.tar.gz'):
mimetype = 'application/x-gzip' mimetype = 'application/x-gzip'