Skip to content

Commit 650769b

Browse files
committed
Merge pull request #6 from graingert/bugfix/fix-package
Fix package so it installs
2 parents db0b699 + 3bb8b64 commit 650769b

File tree

8 files changed

+371
-44
lines changed

8 files changed

+371
-44
lines changed

.travis.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ python:
55
- "2.7"
66
- "3.3"
77
install:
8-
- sudo apt-get update -qq
9-
- sudo apt-get install clamav-daemon clamav-freshclam clamav-unofficial-sigs -qq
10-
- sudo freshclam --quiet
8+
- sudo apt-get install clamav-daemon clamav-freshclam clamav-unofficial-sigs
9+
- sudo freshclam --verbose
1110
- sudo service clamav-daemon start
12-
- pip install . --use-mirrors
11+
- pip install .
1312
script: python setup.py nosetests
1413
matrix:
1514
allow_failures:

MANIFEST.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include CHANGES.rst
2+
include README.rst
3+
include ez_setup.py
4+
5+
recursive-exclude * __pycache__
6+
recursive-exclude * *.py[co]

ez_setup.py

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
#!/usr/bin/env python
2+
"""Bootstrap setuptools installation
3+
4+
To use setuptools in your package's setup.py, include this
5+
file in the same directory and add this to the top of your setup.py::
6+
7+
from ez_setup import use_setuptools
8+
use_setuptools()
9+
10+
To require a specific version of setuptools, set a download
11+
mirror, or use an alternate download directory, simply supply
12+
the appropriate options to ``use_setuptools()``.
13+
14+
This file can also be run as a script to install or upgrade setuptools.
15+
"""
16+
import os
17+
import shutil
18+
import sys
19+
import tempfile
20+
import zipfile
21+
import optparse
22+
import subprocess
23+
import platform
24+
import textwrap
25+
import contextlib
26+
27+
from distutils import log
28+
29+
try:
30+
from urllib.request import urlopen
31+
except ImportError:
32+
from urllib2 import urlopen
33+
34+
try:
35+
from site import USER_SITE
36+
except ImportError:
37+
USER_SITE = None
38+
39+
DEFAULT_VERSION = "5.4.2"
40+
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
41+
42+
def _python_cmd(*args):
43+
"""
44+
Return True if the command succeeded.
45+
"""
46+
args = (sys.executable,) + args
47+
return subprocess.call(args) == 0
48+
49+
50+
def _install(archive_filename, install_args=()):
51+
with archive_context(archive_filename):
52+
# installing
53+
log.warn('Installing Setuptools')
54+
if not _python_cmd('setup.py', 'install', *install_args):
55+
log.warn('Something went wrong during the installation.')
56+
log.warn('See the error message above.')
57+
# exitcode will be 2
58+
return 2
59+
60+
61+
def _build_egg(egg, archive_filename, to_dir):
62+
with archive_context(archive_filename):
63+
# building an egg
64+
log.warn('Building a Setuptools egg in %s', to_dir)
65+
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
66+
# returning the result
67+
log.warn(egg)
68+
if not os.path.exists(egg):
69+
raise IOError('Could not build the egg.')
70+
71+
72+
class ContextualZipFile(zipfile.ZipFile):
73+
"""
74+
Supplement ZipFile class to support context manager for Python 2.6
75+
"""
76+
77+
def __enter__(self):
78+
return self
79+
80+
def __exit__(self, type, value, traceback):
81+
self.close()
82+
83+
def __new__(cls, *args, **kwargs):
84+
"""
85+
Construct a ZipFile or ContextualZipFile as appropriate
86+
"""
87+
if hasattr(zipfile.ZipFile, '__exit__'):
88+
return zipfile.ZipFile(*args, **kwargs)
89+
return super(ContextualZipFile, cls).__new__(cls)
90+
91+
92+
@contextlib.contextmanager
93+
def archive_context(filename):
94+
# extracting the archive
95+
tmpdir = tempfile.mkdtemp()
96+
log.warn('Extracting in %s', tmpdir)
97+
old_wd = os.getcwd()
98+
try:
99+
os.chdir(tmpdir)
100+
with ContextualZipFile(filename) as archive:
101+
archive.extractall()
102+
103+
# going in the directory
104+
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
105+
os.chdir(subdir)
106+
log.warn('Now working in %s', subdir)
107+
yield
108+
109+
finally:
110+
os.chdir(old_wd)
111+
shutil.rmtree(tmpdir)
112+
113+
114+
def _do_download(version, download_base, to_dir, download_delay):
115+
egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
116+
% (version, sys.version_info[0], sys.version_info[1]))
117+
if not os.path.exists(egg):
118+
archive = download_setuptools(version, download_base,
119+
to_dir, download_delay)
120+
_build_egg(egg, archive, to_dir)
121+
sys.path.insert(0, egg)
122+
123+
# Remove previously-imported pkg_resources if present (see
124+
# https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
125+
if 'pkg_resources' in sys.modules:
126+
del sys.modules['pkg_resources']
127+
128+
import setuptools
129+
setuptools.bootstrap_install_from = egg
130+
131+
132+
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
133+
to_dir=os.curdir, download_delay=15):
134+
to_dir = os.path.abspath(to_dir)
135+
rep_modules = 'pkg_resources', 'setuptools'
136+
imported = set(sys.modules).intersection(rep_modules)
137+
try:
138+
import pkg_resources
139+
except ImportError:
140+
return _do_download(version, download_base, to_dir, download_delay)
141+
try:
142+
pkg_resources.require("setuptools>=" + version)
143+
return
144+
except pkg_resources.DistributionNotFound:
145+
return _do_download(version, download_base, to_dir, download_delay)
146+
except pkg_resources.VersionConflict as VC_err:
147+
if imported:
148+
msg = textwrap.dedent("""
149+
The required version of setuptools (>={version}) is not available,
150+
and can't be installed while this script is running. Please
151+
install a more recent version first, using
152+
'easy_install -U setuptools'.
153+
154+
(Currently using {VC_err.args[0]!r})
155+
""").format(VC_err=VC_err, version=version)
156+
sys.stderr.write(msg)
157+
sys.exit(2)
158+
159+
# otherwise, reload ok
160+
del pkg_resources, sys.modules['pkg_resources']
161+
return _do_download(version, download_base, to_dir, download_delay)
162+
163+
def _clean_check(cmd, target):
164+
"""
165+
Run the command to download target. If the command fails, clean up before
166+
re-raising the error.
167+
"""
168+
try:
169+
subprocess.check_call(cmd)
170+
except subprocess.CalledProcessError:
171+
if os.access(target, os.F_OK):
172+
os.unlink(target)
173+
raise
174+
175+
def download_file_powershell(url, target):
176+
"""
177+
Download the file at url to target using Powershell (which will validate
178+
trust). Raise an exception if the command cannot complete.
179+
"""
180+
target = os.path.abspath(target)
181+
ps_cmd = (
182+
"[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
183+
"[System.Net.CredentialCache]::DefaultCredentials; "
184+
"(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)"
185+
% vars()
186+
)
187+
cmd = [
188+
'powershell',
189+
'-Command',
190+
ps_cmd,
191+
]
192+
_clean_check(cmd, target)
193+
194+
def has_powershell():
195+
if platform.system() != 'Windows':
196+
return False
197+
cmd = ['powershell', '-Command', 'echo test']
198+
with open(os.path.devnull, 'wb') as devnull:
199+
try:
200+
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
201+
except Exception:
202+
return False
203+
return True
204+
205+
download_file_powershell.viable = has_powershell
206+
207+
def download_file_curl(url, target):
208+
cmd = ['curl', url, '--silent', '--output', target]
209+
_clean_check(cmd, target)
210+
211+
def has_curl():
212+
cmd = ['curl', '--version']
213+
with open(os.path.devnull, 'wb') as devnull:
214+
try:
215+
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
216+
except Exception:
217+
return False
218+
return True
219+
220+
download_file_curl.viable = has_curl
221+
222+
def download_file_wget(url, target):
223+
cmd = ['wget', url, '--quiet', '--output-document', target]
224+
_clean_check(cmd, target)
225+
226+
def has_wget():
227+
cmd = ['wget', '--version']
228+
with open(os.path.devnull, 'wb') as devnull:
229+
try:
230+
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
231+
except Exception:
232+
return False
233+
return True
234+
235+
download_file_wget.viable = has_wget
236+
237+
def download_file_insecure(url, target):
238+
"""
239+
Use Python to download the file, even though it cannot authenticate the
240+
connection.
241+
"""
242+
src = urlopen(url)
243+
try:
244+
# Read all the data in one block.
245+
data = src.read()
246+
finally:
247+
src.close()
248+
249+
# Write all the data in one block to avoid creating a partial file.
250+
with open(target, "wb") as dst:
251+
dst.write(data)
252+
253+
download_file_insecure.viable = lambda: True
254+
255+
def get_best_downloader():
256+
downloaders = (
257+
download_file_powershell,
258+
download_file_curl,
259+
download_file_wget,
260+
download_file_insecure,
261+
)
262+
viable_downloaders = (dl for dl in downloaders if dl.viable())
263+
return next(viable_downloaders, None)
264+
265+
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
266+
to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
267+
"""
268+
Download setuptools from a specified location and return its filename
269+
270+
`version` should be a valid setuptools version number that is available
271+
as an egg for download under the `download_base` URL (which should end
272+
with a '/'). `to_dir` is the directory where the egg will be downloaded.
273+
`delay` is the number of seconds to pause before an actual download
274+
attempt.
275+
276+
``downloader_factory`` should be a function taking no arguments and
277+
returning a function for downloading a URL to a target.
278+
"""
279+
# making sure we use the absolute path
280+
to_dir = os.path.abspath(to_dir)
281+
zip_name = "setuptools-%s.zip" % version
282+
url = download_base + zip_name
283+
saveto = os.path.join(to_dir, zip_name)
284+
if not os.path.exists(saveto): # Avoid repeated downloads
285+
log.warn("Downloading %s", url)
286+
downloader = downloader_factory()
287+
downloader(url, saveto)
288+
return os.path.realpath(saveto)
289+
290+
def _build_install_args(options):
291+
"""
292+
Build the arguments to 'python setup.py install' on the setuptools package
293+
"""
294+
return ['--user'] if options.user_install else []
295+
296+
def _parse_args():
297+
"""
298+
Parse the command line for options
299+
"""
300+
parser = optparse.OptionParser()
301+
parser.add_option(
302+
'--user', dest='user_install', action='store_true', default=False,
303+
help='install in user site package (requires Python 2.6 or later)')
304+
parser.add_option(
305+
'--download-base', dest='download_base', metavar="URL",
306+
default=DEFAULT_URL,
307+
help='alternative URL from where to download the setuptools package')
308+
parser.add_option(
309+
'--insecure', dest='downloader_factory', action='store_const',
310+
const=lambda: download_file_insecure, default=get_best_downloader,
311+
help='Use internal, non-validating downloader'
312+
)
313+
parser.add_option(
314+
'--version', help="Specify which version to download",
315+
default=DEFAULT_VERSION,
316+
)
317+
options, args = parser.parse_args()
318+
# positional arguments are ignored
319+
return options
320+
321+
def main():
322+
"""Install or upgrade setuptools and EasyInstall"""
323+
options = _parse_args()
324+
archive = download_setuptools(
325+
version=options.version,
326+
download_base=options.download_base,
327+
downloader_factory=options.downloader_factory,
328+
)
329+
return _install(archive, _build_install_args(options))
330+
331+
if __name__ == '__main__':
332+
sys.exit(main())

setup.cfg

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,2 @@
1-
[metadata]
2-
name = clamd
3-
version = 1.0.2.dev0
4-
author = Thomas Grainger
5-
author-email = [email protected]
6-
maintainer = Thomas Grainger
7-
maintainer-email = [email protected]
8-
keywords = python, clamav, antivirus, scanner, virus, libclamav, clamd
9-
summary = Clamd is a python interface to Clamd (Clamav daemon).
10-
description-file =
11-
README.rst
12-
CHANGES.rst
13-
home-page = https://github.com/graingert/python-clamd
14-
requires-dist =
15-
setuptools (0.7)
16-
six (1.3)
17-
classifier = License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
18-
19-
[files]
20-
modules = clamd
21-
extra_files =
22-
setup.py
23-
README.rst
24-
CHANGES.rst
25-
26-
[backwards_compat]
27-
tests-require =
28-
nose (1.3)
29-
30-
[test]
31-
test-suite = nose.collector
32-
331
[nosetests]
342
with-doctest=1

0 commit comments

Comments
 (0)