Skip to content

Commit

Permalink
Merge py3-execfile-5129: Switch to custom execfile() implementation, …
Browse files Browse the repository at this point in the history
…since it missing from Python 3.

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


git-svn-id: svn://svn.twistedmatrix.com/svn/Twisted/trunk@34775 bbbe8e31-12d6-0310-92fd-ac37d47ddeeb
  • Loading branch information
itamarst committed Jul 14, 2012
1 parent 5c209c8 commit eebd1c6
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 27 deletions.
23 changes: 2 additions & 21 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')
Expand Down
4 changes: 3 additions & 1 deletion twisted/lore/htmlbook.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

from twisted.python.compat import execfile


def getNumber(filename):
return None
Expand All @@ -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]
Expand Down
1 change: 1 addition & 0 deletions twisted/names/authority.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions twisted/python/_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 33 additions & 2 deletions twisted/python/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
]
21 changes: 21 additions & 0 deletions twisted/python/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import platform
import sys

from twisted.python.compat import execfile


twisted_subprojects = ["conch", "lore", "mail", "names",
"news", "pair", "runner", "web",
Expand Down Expand Up @@ -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
Expand Down
102 changes: 102 additions & 0 deletions twisted/python/test/test_dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@


import os
import shutil
import sys

from distutils.core import Distribution

Expand Down Expand Up @@ -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):
"""
Expand Down
2 changes: 1 addition & 1 deletion twisted/python/test/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
56 changes: 54 additions & 2 deletions twisted/test/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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



Expand Down Expand Up @@ -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"])
3 changes: 3 additions & 0 deletions twisted/topfiles/5129.misc
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions twisted/web/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down

0 comments on commit eebd1c6

Please sign in to comment.