Permalink
Browse files

Merge py3-execfile-5129: Switch to custom execfile() implementation, …

…since it missing from Python 3.

Author: allenap
Reviewer: exarkun, itamar
Fixes: #5129


git-svn-id: svn://svn.twistedmatrix.com/svn/Twisted/trunk@34775 bbbe8e31-12d6-0310-92fd-ac37d47ddeeb
  • Loading branch information...
1 parent 5c209c8 commit eebd1c6d9014bc0296d1c6e52d12faffba6b9fee itamarst committed Jul 14, 2012
View
@@ -16,25 +16,6 @@
import sys, os
-def getExtensions():
- """
- Get all extensions from core and all subprojects.
- """
- extensions = []
-
- if not sys.platform.startswith('java'):
- for dir in os.listdir("twisted") + [""]:
- topfiles = os.path.join("twisted", dir, "topfiles")
- if os.path.isdir(topfiles):
- ns = {}
- setup_py = os.path.join(topfiles, "setup.py")
- execfile(setup_py, ns, ns)
- if "extensions" in ns:
- extensions.extend(ns["extensions"])
-
- return extensions
-
-
def main(args):
"""
Invoke twisted.python.dist with the appropriate metadata about the
@@ -43,8 +24,8 @@ def main(args):
if os.path.exists('twisted'):
sys.path.insert(0, '.')
from twisted import copyright
- from twisted.python.dist import getDataFiles, getScripts, getPackages, \
- setup, twisted_subprojects
+ from twisted.python.dist import getDataFiles, getExtensions, getScripts, \
+ getPackages, setup, twisted_subprojects
# "" is included because core scripts are directly in bin/
projects = [''] + [x for x in os.listdir('bin')
@@ -1,6 +1,8 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
+from twisted.python.compat import execfile
+
def getNumber(filename):
return None
@@ -24,7 +26,7 @@ def __init__(self, filename):
Index = self.Index
if filename:
- execfile(filename)
+ execfile(filename, globals())
def getFiles(self):
return [c[0] for c in self.chapters]
@@ -12,6 +12,7 @@
from twisted.names import dns
from twisted.internet import defer
from twisted.python import failure
+from twisted.python.compat import execfile
import common
@@ -25,6 +25,7 @@
from twisted.python.versions import Version
from twisted.python.filepath import FilePath
from twisted.python.dist import twisted_subprojects
+from twisted.python.compat import execfile
# This import is an example of why you shouldn't use this module unless you're
# radix
@@ -139,12 +139,12 @@ def __init__(self, *args):
'set_connect_state', 'set_accept_state',
'connect_ex', 'sendall'):
- exec """def %s(self, *args):
+ exec("""def %s(self, *args):
self._lock.acquire()
try:
return apply(self._ssl_conn.%s, args)
finally:
- self._lock.release()\n""" % (f, f)
+ self._lock.release()\n""" % (f, f))
sys.modules['OpenSSL.tsafe'] = tsafe
import operator
@@ -175,3 +175,34 @@ def __call__(self, obj):
from functools import reduce
except ImportError:
reduce = reduce
+
+
+
+def execfile(filename, globals, locals=None):
+ """
+ Execute a Python script in the given namespaces.
+
+ Similar to the execfile builtin, but a namespace is mandatory, partly
+ because that's a sensible thing to require, and because otherwise we'd
+ have to do some frame hacking.
+
+ This is a compatibility implementation for Python 3 porting, to avoid the
+ use of the deprecated builtin C{execfile} function.
+ """
+ if locals is None:
+ locals = globals
+ fin = open(filename, "rbU")
+ try:
+ source = fin.read()
+ finally:
+ fin.close()
+ code = compile(source, filename, "exec")
+ exec(code, globals, locals)
+
+
+__all__ = [
+ "execfile",
+ "frozenset",
+ "reduce",
+ "set",
+ ]
@@ -15,6 +15,8 @@
import platform
import sys
+from twisted.python.compat import execfile
+
twisted_subprojects = ["conch", "lore", "mail", "names",
"news", "pair", "runner", "web",
@@ -208,6 +210,25 @@ def getDataFiles(dname, ignore=None, parent=None):
return result
+def getExtensions():
+ """
+ Get all extensions from core and all subprojects.
+ """
+ extensions = []
+
+ if not sys.platform.startswith('java'):
+ for dir in os.listdir("twisted") + [""]:
+ topfiles = os.path.join("twisted", dir, "topfiles")
+ if os.path.isdir(topfiles):
+ ns = {}
+ setup_py = os.path.join(topfiles, "setup.py")
+ execfile(setup_py, ns, ns)
+ if "extensions" in ns:
+ extensions.extend(ns["extensions"])
+
+ return extensions
+
+
def getPackages(dname, pkgname=None, results=None, ignore=None, parent=None):
"""
Get all packages which are under dname. This is necessary for
@@ -7,6 +7,8 @@
import os
+import shutil
+import sys
from distutils.core import Distribution
@@ -55,6 +57,106 @@ def test_win32Definition(self):
self.assertEqual(ext.define_macros, [("whatever", 2), ("WIN32", 1)])
+class GetExtensionsTest(TestCase):
+ """
+ Tests for L{dist.getExtensions}.
+ """
+
+ setupTemplate = (
+ "from twisted.python.dist import ConditionalExtension\n"
+ "extensions = [\n"
+ " ConditionalExtension(\n"
+ " '%s', ['twisted/some/thing.c'],\n"
+ " condition=lambda builder: True)\n"
+ " ]\n")
+
+ def setUp(self):
+ self.basedir = FilePath(self.mktemp()).child("twisted")
+ self.basedir.makedirs()
+ self.addCleanup(os.chdir, os.getcwd())
+ os.chdir(self.basedir.parent().path)
+
+
+ def writeSetup(self, name, *path):
+ """
+ Write out a C{setup.py} file to a location determined by
+ L{self.basedir} and L{path}. L{self.setupTemplate} is used to
+ generate its contents.
+ """
+ outdir = self.basedir.descendant(path)
+ outdir.makedirs()
+ setup = outdir.child("setup.py")
+ setup.setContent(self.setupTemplate % (name,))
+
+
+ def writeEmptySetup(self, *path):
+ """
+ Write out an empty C{setup.py} file to a location determined by
+ L{self.basedir} and L{path}.
+ """
+ outdir = self.basedir.descendant(path)
+ outdir.makedirs()
+ outdir.child("setup.py").setContent("")
+
+
+ def assertExtensions(self, expected):
+ """
+ Assert that the given names match the (sorted) names of discovered
+ extensions.
+ """
+ extensions = dist.getExtensions()
+ names = [extension.name for extension in extensions]
+ self.assertEqual(sorted(names), expected)
+
+
+ def test_getExtensions(self):
+ """
+ Files named I{setup.py} in I{twisted/topfiles} and I{twisted/*/topfiles}
+ are executed with L{execfile} in order to discover the extensions they
+ declare.
+ """
+ self.writeSetup("twisted.transmutate", "topfiles")
+ self.writeSetup("twisted.tele.port", "tele", "topfiles")
+ self.assertExtensions(["twisted.tele.port", "twisted.transmutate"])
+
+
+ def test_getExtensionsTooDeep(self):
+ """
+ Files named I{setup.py} in I{topfiles} directories are not considered if
+ they are too deep in the directory hierarchy.
+ """
+ self.writeSetup("twisted.trans.mog.rify", "trans", "mog", "topfiles")
+ self.assertExtensions([])
+
+
+ def test_getExtensionsNotTopfiles(self):
+ """
+ The folder in which I{setup.py} is discovered must be called I{topfiles}
+ otherwise it is ignored.
+ """
+ self.writeSetup("twisted.metamorphosis", "notfiles")
+ self.assertExtensions([])
+
+
+ def test_getExtensionsNotSupportedOnJava(self):
+ """
+ Extensions are not supported on Java-based platforms.
+ """
+ self.addCleanup(setattr, sys, "platform", sys.platform)
+ sys.platform = "java"
+ self.writeSetup("twisted.sorcery", "topfiles")
+ self.assertExtensions([])
+
+
+ def test_getExtensionsExtensionsLocalIsOptional(self):
+ """
+ It is acceptable for extensions to not define the C{extensions} local
+ variable.
+ """
+ self.writeEmptySetup("twisted.necromancy", "topfiles")
+ self.assertExtensions([])
+
+
class GetVersionTest(TestCase):
"""
@@ -20,7 +20,7 @@
from twisted.trial.unittest import TestCase
-from twisted.python.compat import set
+from twisted.python.compat import execfile, set
from twisted.python.procutils import which
from twisted.python import release
from twisted.python.filepath import FilePath
@@ -6,11 +6,12 @@
Tests for L{twisted.python.compat}.
"""
-import types, socket
+import os, tempfile, types, socket
from twisted.trial import unittest
-from twisted.python.compat import set, frozenset, reduce
+from twisted.python.filepath import FilePath
+from twisted.python.compat import set, frozenset, reduce, execfile
@@ -198,3 +199,54 @@ def test_reduce(self):
"""
self.assertEqual(15, reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]))
self.assertEqual(16, reduce(lambda x, y: x + y, [1, 2, 3, 4, 5], 1))
+
+
+
+class ExecfileCompatTestCase(unittest.TestCase):
+ """
+ Tests for the Python 3-friendly L{execfile} implementation.
+ """
+
+ def writeScript(self, content):
+ """
+ Write L{content} to a new temporary file, returning the L{FilePath}
+ for the new file.
+ """
+ script = FilePath(self.mktemp())
+ script.setContent(content.encode("ascii"))
+ return script
+
+
+ def test_execfileGlobals(self):
+ """
+ L{execfile} executes the specified file in the given global namespace.
+ """
+ script = self.writeScript("foo += 1\n")
+ globalNamespace = {"foo": 1}
+ execfile(script.path, globalNamespace)
+ self.assertEqual(2, globalNamespace["foo"])
+
+
+ def test_execfileGlobalsAndLocals(self):
+ """
+ L{execfile} executes the specified file in the given global and local
+ namespaces.
+ """
+ script = self.writeScript("foo += 1\n")
+ globalNamespace = {"foo": 10}
+ localNamespace = {"foo": 20}
+ execfile(script.path, globalNamespace, localNamespace)
+ self.assertEqual(10, globalNamespace["foo"])
+ self.assertEqual(21, localNamespace["foo"])
+
+
+ def test_execfileUniversalNewlines(self):
+ """
+ L{execfile} reads in the specified file using universal newlines so
+ that scripts written on one platform will work on another.
+ """
+ for lineEnding in "\n", "\r", "\r\n":
+ script = self.writeScript("foo = 'okay'" + lineEnding)
+ globalNamespace = {"foo": None}
+ execfile(script.path, globalNamespace)
+ self.assertEqual("okay", globalNamespace["foo"])
@@ -0,0 +1,3 @@
+Replace usage of execfile() with t.p.compat.execfile(), a new function
+that wraps execfile() on Python 2.x and provides equivalent
+functionality on Python 3.x.
@@ -14,6 +14,7 @@
import StringIO
from twisted import copyright
+from twisted.python.compat import execfile
from twisted.web import http, server, static, resource, html

0 comments on commit eebd1c6

Please sign in to comment.