Skip to content

Commit 8de9bc1

Browse files
committed
Make installable via pip and update buildout to latest bootstrap and builout
1 parent 938eaae commit 8de9bc1

File tree

17 files changed

+147
-186
lines changed

17 files changed

+147
-186
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include fakeemail/templates/*.html

README.rst

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,23 @@ FakeEmail
44

55
FakeEmail is an email server, for use in a development environment where routing email is either undesirable, or impossible to manage.
66

7-
The aim of the system is to sit on a port, accept SMTP connections and to display a list of the emails that it has received.
7+
The aim of the system is to open a port, accept SMTP connections and to display a list of the emails that it has received.
88
At no point will the emails route further on, so there is no requirement for the addresses to be valid, or to wait around waiting for the email to arrive in your inbox.
99

1010
Installation
1111
------------
1212

13+
Via pip
14+
~~~~~~~
15+
16+
FakeEmail is installable via pip:
17+
```bash
18+
pip install fakeemail
19+
```
20+
21+
From Source
22+
~~~~~~~~~~~
23+
1324
FakeEmail is installed using the buildout system and depends on Twisted for it's server capabilites. Installation is as straight forward as possible::
1425

1526
git clone git://github.com/tomwardill/FakeEmail.git
@@ -21,6 +32,20 @@ FakeEmail is installed using the buildout system and depends on Twisted for it's
2132
Usage
2233
-----
2334

35+
From pip install
36+
~~~~~~~~~~~~~~~~
37+
38+
If you installed fakeemail via pip, then running is simple:
39+
40+
```bash
41+
fakeemail 2025 8080 0.0.0.0
42+
```
43+
44+
This will run on SMTP port `2025`, the web interface on `8080` and listen on all network interfaces. Running on port 25 is possible, but you will need to start with root privileges.
45+
46+
Installed from source
47+
~~~~~~~~~~~~~~~~~~~~~
48+
2449
FakeEmail installs itself as a plugin into the local buildout twisted environment. This makes execution reasonably simple::
2550

2651
bin/twistd -n fakeemail

bootstrap.py

Lines changed: 85 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -18,74 +18,14 @@
1818
use the -c option to specify an alternate configuration file.
1919
"""
2020

21-
import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess
22-
from optparse import OptionParser
23-
24-
if sys.platform == 'win32':
25-
def quote(c):
26-
if ' ' in c:
27-
return '"%s"' % c # work around spawn lamosity on windows
28-
else:
29-
return c
30-
else:
31-
quote = str
32-
33-
# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
34-
stdout, stderr = subprocess.Popen(
35-
[sys.executable, '-Sc',
36-
'try:\n'
37-
' import ConfigParser\n'
38-
'except ImportError:\n'
39-
' print 1\n'
40-
'else:\n'
41-
' print 0\n'],
42-
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
43-
has_broken_dash_S = bool(int(stdout.strip()))
21+
import os
22+
import shutil
23+
import sys
24+
import tempfile
4425

45-
# In order to be more robust in the face of system Pythons, we want to
46-
# run without site-packages loaded. This is somewhat tricky, in
47-
# particular because Python 2.6's distutils imports site, so starting
48-
# with the -S flag is not sufficient. However, we'll start with that:
49-
if not has_broken_dash_S and 'site' in sys.modules:
50-
# We will restart with python -S.
51-
args = sys.argv[:]
52-
args[0:0] = [sys.executable, '-S']
53-
args = map(quote, args)
54-
os.execv(sys.executable, args)
55-
# Now we are running with -S. We'll get the clean sys.path, import site
56-
# because distutils will do it later, and then reset the path and clean
57-
# out any namespace packages from site-packages that might have been
58-
# loaded by .pth files.
59-
clean_path = sys.path[:]
60-
import site
61-
sys.path[:] = clean_path
62-
for k, v in sys.modules.items():
63-
if k in ('setuptools', 'pkg_resources') or (
64-
hasattr(v, '__path__') and
65-
len(v.__path__)==1 and
66-
not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
67-
# This is a namespace package. Remove it.
68-
sys.modules.pop(k)
69-
70-
is_jython = sys.platform.startswith('java')
71-
72-
setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
73-
distribute_source = 'http://python-distribute.org/distribute_setup.py'
26+
from optparse import OptionParser
7427

75-
# parsing arguments
76-
def normalize_to_url(option, opt_str, value, parser):
77-
if value:
78-
if '://' not in value: # It doesn't smell like a URL.
79-
value = 'file://%s' % (
80-
urllib.pathname2url(
81-
os.path.abspath(os.path.expanduser(value))),)
82-
if opt_str == '--download-base' and not value.endswith('/'):
83-
# Download base needs a trailing slash to make the world happy.
84-
value += '/'
85-
else:
86-
value = None
87-
name = opt_str[2:].replace('-', '_')
88-
setattr(parser.values, name, value)
28+
tmpeggs = tempfile.mkdtemp()
8929

9030
usage = '''\
9131
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
@@ -95,31 +35,13 @@ def normalize_to_url(option, opt_str, value, parser):
9535
Simply run this script in a directory containing a buildout.cfg, using the
9636
Python that you want bin/buildout to use.
9737
98-
Note that by using --setup-source and --download-base to point to
99-
local resources, you can keep this script from going over the network.
38+
Note that by using --find-links to point to local resources, you can keep
39+
this script from going over the network.
10040
'''
10141

10242
parser = OptionParser(usage=usage)
103-
parser.add_option("-v", "--version", dest="version",
104-
help="use a specific zc.buildout version")
105-
parser.add_option("-d", "--distribute",
106-
action="store_true", dest="use_distribute", default=False,
107-
help="Use Distribute rather than Setuptools.")
108-
parser.add_option("--setup-source", action="callback", dest="setup_source",
109-
callback=normalize_to_url, nargs=1, type="string",
110-
help=("Specify a URL or file location for the setup file. "
111-
"If you use Setuptools, this will default to " +
112-
setuptools_source + "; if you use Distribute, this "
113-
"will default to " + distribute_source +"."))
114-
parser.add_option("--download-base", action="callback", dest="download_base",
115-
callback=normalize_to_url, nargs=1, type="string",
116-
help=("Specify a URL or directory for downloading "
117-
"zc.buildout and either Setuptools or Distribute. "
118-
"Defaults to PyPI."))
119-
parser.add_option("--eggs",
120-
help=("Specify a directory for storing eggs. Defaults to "
121-
"a temporary directory that is deleted when the "
122-
"bootstrap script completes."))
43+
parser.add_option("-v", "--version", help="use a specific zc.buildout version")
44+
12345
parser.add_option("-t", "--accept-buildout-test-releases",
12446
dest='accept_buildout_test_releases',
12547
action="store_true", default=False,
@@ -129,101 +51,89 @@ def normalize_to_url(option, opt_str, value, parser):
12951
"extensions for you. If you use this flag, "
13052
"bootstrap and buildout will get the newest releases "
13153
"even if they are alphas or betas."))
132-
parser.add_option("-c", None, action="store", dest="config_file",
133-
help=("Specify the path to the buildout configuration "
134-
"file to be used."))
135-
parser.add_option("-e", "--distribute-version", action="store", dest="distribute_version",
136-
help=("Specify the version for distribute."))
54+
parser.add_option("-c", "--config-file",
55+
help=("Specify the path to the buildout configuration "
56+
"file to be used."))
57+
parser.add_option("-f", "--find-links",
58+
help=("Specify a URL to search for buildout releases"))
59+
parser.add_option("--allow-site-packages",
60+
action="store_true", default=False,
61+
help=("Let bootstrap.py use existing site packages"))
13762

13863

13964
options, args = parser.parse_args()
14065

141-
# if -c was provided, we push it back into args for buildout's main function
142-
if options.config_file is not None:
143-
args += ['-c', options.config_file]
144-
145-
if options.eggs:
146-
eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
147-
else:
148-
eggs_dir = tempfile.mkdtemp()
149-
150-
if options.setup_source is None:
151-
if options.use_distribute:
152-
options.setup_source = distribute_source
153-
else:
154-
options.setup_source = setuptools_source
155-
156-
if options.accept_buildout_test_releases:
157-
args.append('buildout:accept-buildout-test-releases=true')
158-
args.append('bootstrap')
159-
66+
######################################################################
67+
# load/install setuptools
16068

16169
try:
162-
import pkg_resources
163-
import setuptools # A flag. Sometimes pkg_resources is installed alone.
164-
if not hasattr(pkg_resources, '_distribute'):
165-
raise ImportError
70+
if options.allow_site_packages:
71+
import setuptools
72+
import pkg_resources
73+
from urllib.request import urlopen
16674
except ImportError:
167-
ez_code = urllib2.urlopen(
168-
options.setup_source).read().replace('\r\n', '\n')
169-
ez = {}
170-
exec ez_code in ez
171-
setup_args = dict(to_dir=eggs_dir, download_delay=0)
172-
if options.distribute_version:
173-
setup_args['version'] = options.distribute_version
174-
if options.download_base:
175-
setup_args['download_base'] = options.download_base
176-
if options.use_distribute:
177-
setup_args['no_fake'] = True
178-
ez['use_setuptools'](**setup_args)
179-
if 'pkg_resources' in sys.modules:
180-
reload(sys.modules['pkg_resources'])
181-
import pkg_resources
182-
# This does not (always?) update the default working set. We will
183-
# do it.
184-
for path in sys.path:
185-
if path not in pkg_resources.working_set.entries:
186-
pkg_resources.working_set.add_entry(path)
75+
from urllib2 import urlopen
76+
77+
ez = {}
78+
exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
79+
80+
if not options.allow_site_packages:
81+
# ez_setup imports site, which adds site packages
82+
# this will remove them from the path to ensure that incompatible versions
83+
# of setuptools are not in the path
84+
import site
85+
# inside a virtualenv, there is no 'getsitepackages'.
86+
# We can't remove these reliably
87+
if hasattr(site, 'getsitepackages'):
88+
for sitepackage_path in site.getsitepackages():
89+
sys.path[:] = [x for x in sys.path if sitepackage_path not in x]
90+
91+
setup_args = dict(to_dir=tmpeggs, download_delay=0)
92+
ez['use_setuptools'](**setup_args)
93+
import setuptools
94+
import pkg_resources
95+
96+
# This does not (always?) update the default working set. We will
97+
# do it.
98+
for path in sys.path:
99+
if path not in pkg_resources.working_set.entries:
100+
pkg_resources.working_set.add_entry(path)
101+
102+
######################################################################
103+
# Install buildout
187104

188-
cmd = [quote(sys.executable),
189-
'-c',
190-
quote('from setuptools.command.easy_install import main; main()'),
191-
'-mqNxd',
192-
quote(eggs_dir)]
105+
ws = pkg_resources.working_set
193106

194-
if not has_broken_dash_S:
195-
cmd.insert(1, '-S')
107+
cmd = [sys.executable, '-c',
108+
'from setuptools.command.easy_install import main; main()',
109+
'-mZqNxd', tmpeggs]
196110

197-
find_links = options.download_base
198-
if not find_links:
199-
find_links = os.environ.get('bootstrap-testing-find-links')
111+
find_links = os.environ.get(
112+
'bootstrap-testing-find-links',
113+
options.find_links or
114+
('http://downloads.buildout.org/'
115+
if options.accept_buildout_test_releases else None)
116+
)
200117
if find_links:
201-
cmd.extend(['-f', quote(find_links)])
118+
cmd.extend(['-f', find_links])
202119

203-
if options.use_distribute:
204-
setup_requirement = 'distribute'
205-
else:
206-
setup_requirement = 'setuptools'
207-
ws = pkg_resources.working_set
208-
setup_requirement_path = ws.find(
209-
pkg_resources.Requirement.parse(setup_requirement)).location
210-
env = dict(
211-
os.environ,
212-
PYTHONPATH=setup_requirement_path)
120+
setuptools_path = ws.find(
121+
pkg_resources.Requirement.parse('setuptools')).location
213122

214123
requirement = 'zc.buildout'
215124
version = options.version
216125
if version is None and not options.accept_buildout_test_releases:
217126
# Figure out the most recent final version of zc.buildout.
218127
import setuptools.package_index
219128
_final_parts = '*final-', '*final'
129+
220130
def _final_version(parsed_version):
221131
for part in parsed_version:
222132
if (part[:1] == '*') and (part not in _final_parts):
223133
return False
224134
return True
225135
index = setuptools.package_index.PackageIndex(
226-
search_path=[setup_requirement_path])
136+
search_path=[setuptools_path])
227137
if find_links:
228138
index.add_find_links((find_links,))
229139
req = pkg_resources.Requirement.parse(requirement)
@@ -245,22 +155,24 @@ def _final_version(parsed_version):
245155
requirement = '=='.join((requirement, version))
246156
cmd.append(requirement)
247157

248-
if is_jython:
249-
import subprocess
250-
exitcode = subprocess.Popen(cmd, env=env).wait()
251-
else: # Windows prefers this, apparently; otherwise we would prefer subprocess
252-
exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
253-
if exitcode != 0:
254-
sys.stdout.flush()
255-
sys.stderr.flush()
256-
print ("An error occurred when trying to install zc.buildout. "
257-
"Look above this message for any errors that "
258-
"were output by easy_install.")
259-
sys.exit(exitcode)
158+
import subprocess
159+
if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0:
160+
raise Exception(
161+
"Failed to execute command:\n%s" % repr(cmd)[1:-1])
260162

261-
ws.add_entry(eggs_dir)
163+
######################################################################
164+
# Import and run buildout
165+
166+
ws.add_entry(tmpeggs)
262167
ws.require(requirement)
263168
import zc.buildout.buildout
169+
170+
if not [a for a in args if '=' not in a]:
171+
args.append('bootstrap')
172+
173+
# if -c was provided, we push it back into args for buildout' main function
174+
if options.config_file is not None:
175+
args[0:0] = ['-c', options.config_file]
176+
264177
zc.buildout.buildout.main(args)
265-
if not options.eggs: # clean up temporary egg directory
266-
shutil.rmtree(eggs_dir)
178+
shutil.rmtree(tmpeggs)

buildout.cfg

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
[buildout]
2-
develop = src/
2+
develop = .
33
parts =
44
fakeemail-eggs
55
python
66
fakeemail
77
smtp-port = 2025
88
web-port = 8000
99
interface = 0.0.0.0
10-
versions = versions
1110

1211
[fakeemail-eggs]
1312
recipe = zc.recipe.egg:scripts
@@ -37,8 +36,3 @@ args =
3736
-s ${buildout:smtp-port}
3837
-w ${buildout:web-port}
3938
-i ${buildout:interface}
40-
41-
[versions]
42-
zc.buildout = 1.5.2
43-
distribute = 0.6.19
44-
zc.recipe.egg = 1.3.2

0 commit comments

Comments
 (0)