Skip to content

Commit b9eea97

Browse files
committed
Merge pull request #7 from rackerlabs/refactor_package
Refactor Package
2 parents d0ed776 + 64ea01c commit b9eea97

File tree

7 files changed

+193
-114
lines changed

7 files changed

+193
-114
lines changed

circle.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@ machine:
22
python:
33
version: 2.7
44

5-
dependencies:
6-
post:
7-
- rm -r ~/virtualenvs
8-
95
test:
106
pre:
117
- pip install -r test-requirements.txt
128
override:
139
- flake8 .
10+
- py.test

lambda_uploader/package.py

Lines changed: 109 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -25,105 +25,116 @@
2525
ZIPFILE_NAME = 'lambda_function.zip'
2626

2727

28-
def cleanup(path):
29-
temp_workspace = os.path.join(path, TEMP_WORKSPACE_NAME)
30-
if os.path.isdir(temp_workspace):
31-
shutil.rmtree(temp_workspace)
32-
zip_file = os.path.join(path, ZIPFILE_NAME)
33-
if os.path.isfile(zip_file):
34-
os.remove(zip_file)
28+
def build_package(path, requirements):
29+
pkg = Package(path)
3530

31+
pkg.clean_workspace()
32+
pkg.clean_zipfile()
33+
pkg.prepare_workspace()
34+
pkg.install_requirements(requirements)
35+
pkg.package()
36+
return pkg
3637

37-
def build_package(path, requirements):
38-
temp_workspace = os.path.join(path, TEMP_WORKSPACE_NAME)
39-
# Calling cleanup first to cover a previous failed run
40-
cleanup(path)
41-
42-
# Setup temporary workspace
43-
os.mkdir(temp_workspace)
44-
45-
pkg_venv = os.path.join(temp_workspace, 'venv')
46-
proc = Popen(["virtualenv", pkg_venv], stdout=PIPE, stderr=PIPE)
47-
stdout, stderr = proc.communicate()
48-
LOG.debug("Virtualenv stdout: %s" % stdout)
49-
LOG.debug("Virtualenv stderr: %s" % stderr)
50-
51-
if proc.returncode is not 0:
52-
raise Exception('virtualenv returned unsuccessfully')
53-
54-
cmd = None
55-
if requirements:
56-
LOG.debug("Installing requirements found %s in config" % requirements)
57-
cmd = [os.path.join(pkg_venv, _venv_pip()),
58-
'install', " ".join(requirements)]
59-
60-
elif os.path.isfile("requirements.txt"):
61-
# Pip install
62-
LOG.debug("Installing requirements from requirements.txt file")
63-
cmd = [os.path.join(pkg_venv, _venv_pip()),
64-
"install", "-r", "requirements.txt"]
65-
66-
if cmd is not None:
67-
prc = Popen(cmd, stdout=PIPE, stderr=PIPE)
68-
stdout, stderr = prc.communicate()
69-
LOG.debug("Pip stdout: %s" % stdout)
70-
LOG.debug("Pip stderr: %s" % stderr)
38+
39+
class Package(object):
40+
def __init__(self, path):
41+
self._path = path
42+
self._temp_workspace = os.path.join(path,
43+
TEMP_WORKSPACE_NAME)
44+
self.zip_file = os.path.join(path, ZIPFILE_NAME)
45+
46+
self._pkg_venv = os.path.join(self._temp_workspace, 'venv')
47+
self._venv_pip = 'bin/pip'
48+
if sys.platform == 'win32' or sys.platform == 'cygwin':
49+
self._venv_pip = 'Scripts\pip.exe'
50+
51+
def clean_workspace(self):
52+
if os.path.isdir(self._temp_workspace):
53+
shutil.rmtree(self._temp_workspace)
54+
55+
def clean_zipfile(self):
56+
if os.path.isfile(self.zip_file):
57+
os.remove(self.zip_file)
58+
59+
def prepare_workspace(self):
60+
# Setup temporary workspace
61+
os.mkdir(self._temp_workspace)
62+
63+
proc = Popen(["virtualenv", self._pkg_venv], stdout=PIPE, stderr=PIPE)
64+
stdout, stderr = proc.communicate()
65+
LOG.debug("Virtualenv stdout: %s" % stdout)
66+
LOG.debug("Virtualenv stderr: %s" % stderr)
7167

7268
if proc.returncode is not 0:
73-
raise Exception('pip returned unsuccessfully')
74-
package = os.path.join(temp_workspace, 'lambda_package')
75-
76-
# Copy site packages into package base
77-
LOG.info('Copying site packages')
78-
79-
site_packages = 'lib/python2.7/site-packages'
80-
if sys.platform == 'win32' or sys.platform == 'cygwin':
81-
site_packages = 'lib\\site-packages'
82-
83-
shutil.copytree(os.path.join(pkg_venv, site_packages),
84-
package)
85-
_copy_src_files(path, package)
86-
_create_zip(package, path)
87-
88-
89-
def _venv_pip():
90-
if sys.platform == 'win32' or sys.platform == 'cygwin':
91-
return 'Scripts\pip.exe'
92-
else:
93-
return 'bin/pip'
94-
95-
96-
def _copy_src_files(src, package):
97-
LOG.info('Copying source files')
98-
# Re-create cwd directory structure
99-
for root, subdirs, files in os.walk(src):
100-
for subdir in subdirs:
101-
subdir_path = os.path.join(root, subdir)
102-
if TEMP_WORKSPACE_NAME in subdir_path:
103-
continue
104-
fpath = os.path.join(package, subdir)
105-
LOG.debug("Creating directory %s" % fpath)
106-
os.mkdir(fpath)
107-
108-
for filename in files:
109-
path = os.path.join(root, filename)
110-
if TEMP_WORKSPACE_NAME in path:
111-
continue
112-
113-
shutil.copy(path, package)
114-
115-
116-
def _create_zip(src, dest):
117-
zfile = os.path.join(dest, ZIPFILE_NAME)
118-
LOG.info('Creating zipfile')
119-
zf = zipfile.ZipFile(zfile, "w", zipfile.ZIP_DEFLATED)
120-
abs_src = os.path.abspath(src)
121-
for root, _, files in os.walk(src):
122-
for filename in files:
123-
absname = os.path.abspath(os.path.join(root, filename))
124-
arcname = absname[len(abs_src) + 1:]
125-
LOG.debug('Zipping %s as %s'
126-
% (os.path.join(root, filename), arcname))
127-
128-
zf.write(absname, arcname)
129-
zf.close()
69+
raise Exception('virtualenv returned unsuccessfully')
70+
71+
def install_requirements(self, requirements):
72+
cmd = None
73+
if requirements:
74+
LOG.debug("Installing requirements found %s in config"
75+
% requirements)
76+
cmd = [os.path.join(self._pkg_venv, self._venv_pip),
77+
'install', " ".join(requirements)]
78+
79+
elif os.path.isfile("requirements.txt"):
80+
# Pip install
81+
LOG.debug("Installing requirements from requirements.txt file")
82+
cmd = [os.path.join(self._pkg_venv, self._venv_pip),
83+
"install", "-r", "requirements.txt"]
84+
85+
if cmd is not None:
86+
prc = Popen(cmd, stdout=PIPE, stderr=PIPE)
87+
stdout, stderr = prc.communicate()
88+
LOG.debug("Pip stdout: %s" % stdout)
89+
LOG.debug("Pip stderr: %s" % stderr)
90+
91+
if prc.returncode is not 0:
92+
raise Exception('pip returned unsuccessfully')
93+
94+
def package(self):
95+
package = os.path.join(self._temp_workspace, 'lambda_package')
96+
97+
# Copy site packages into package base
98+
LOG.info('Copying site packages')
99+
site_packages = 'lib/python2.7/site-packages'
100+
if sys.platform == 'win32' or sys.platform == 'cygwin':
101+
site_packages = 'lib\\site-packages'
102+
103+
shutil.copytree(os.path.join(self._pkg_venv, site_packages),
104+
package)
105+
self._copy_src_files(package)
106+
self._create_zip(package)
107+
108+
def _copy_src_files(self, package):
109+
LOG.info('Copying source files')
110+
# Re-create cwd directory structure
111+
for root, subdirs, files in os.walk(self._path):
112+
for subdir in subdirs:
113+
subdir_path = os.path.join(root, subdir)
114+
if TEMP_WORKSPACE_NAME in subdir_path:
115+
continue
116+
fpath = os.path.join(package, subdir)
117+
LOG.debug("Creating directory %s" % fpath)
118+
os.mkdir(fpath)
119+
120+
for filename in files:
121+
path = os.path.join(root, filename)
122+
if TEMP_WORKSPACE_NAME in path:
123+
continue
124+
125+
shutil.copy(path, package)
126+
127+
def _create_zip(self, src):
128+
zfile = os.path.join(self._path, ZIPFILE_NAME)
129+
LOG.info('Creating zipfile')
130+
zf = zipfile.ZipFile(zfile, "w", zipfile.ZIP_DEFLATED)
131+
abs_src = os.path.abspath(src)
132+
for root, _, files in os.walk(src):
133+
for filename in files:
134+
absname = os.path.abspath(os.path.join(root, filename))
135+
arcname = absname[len(abs_src) + 1:]
136+
LOG.debug('Zipping %s as %s'
137+
% (os.path.join(root, filename), arcname))
138+
139+
zf.write(absname, arcname)
140+
zf.close()

lambda_uploader/shell.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@ def _execute(args):
4949

5050
cfg = config.Config(pth)
5151
_print('Building Package')
52-
package.build_package(pth, cfg.requirements)
53-
if not args.no_upload:
54-
_print('Uploading Package')
55-
uploader.upload_package(pth, cfg)
52+
pkg = package.build_package(pth, cfg.requirements)
5653

5754
if not args.no_clean:
58-
package.cleanup(pth)
55+
pkg.clean_workspace()
56+
57+
if not args.no_upload:
58+
_print('Uploading Package')
59+
uploader.upload_package(pkg, cfg)
60+
pkg.clean_zipfile()
5961

6062
_print('Fin')
6163

@@ -74,7 +76,7 @@ def main(arv=None):
7476

7577
parser.add_argument('--no-clean', dest='no_clean',
7678
action='store_const',
77-
help='dont cleanup any files after uploading',
79+
help='dont cleanup the temporary workspace',
7880
const=True)
7981
parser.add_argument('function_dir', default=getcwd(), nargs='?',
8082
help='lambda function directory')

lambda_uploader/uploader.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,11 @@
1515
import boto3
1616
import logging
1717

18-
from os import path
19-
from lambda_uploader import package
20-
2118
LOG = logging.getLogger(__name__)
2219

2320

24-
def upload_package(function_dir, config):
25-
zfile = path.join(function_dir, package.ZIPFILE_NAME)
26-
with open(zfile, "rb") as fil:
21+
def upload_package(pkg, config):
22+
with open(pkg.zip_file, "rb") as fil:
2723
zip_file = fil.read()
2824

2925
client = boto3.client('lambda', region_name=config.region)

test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pylint==1.4.1
22
flake8==2.3.0
3+
pytest==2.8.2

test/test_package.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import os
2+
import sys
3+
4+
from shutil import rmtree
5+
from os import path
6+
from lambda_uploader import package
7+
8+
TESTING_TEMP_DIR = '.testing_temp'
9+
10+
11+
def setup_module(module):
12+
print('calling setup')
13+
os.mkdir(TESTING_TEMP_DIR)
14+
15+
16+
def teardown_module(module):
17+
print('calling teardown')
18+
rmtree(TESTING_TEMP_DIR)
19+
20+
21+
def test_package_zip_location():
22+
pkg = package.Package(TESTING_TEMP_DIR)
23+
assert pkg.zip_file == '.testing_temp/lambda_function.zip'
24+
25+
26+
def test_package_clean_workspace():
27+
temp_workspace = path.join(TESTING_TEMP_DIR,
28+
'.lambda_package')
29+
os.mkdir(temp_workspace)
30+
31+
pkg = package.Package(TESTING_TEMP_DIR)
32+
pkg.clean_workspace()
33+
assert path.isdir(temp_workspace) == False
34+
35+
36+
def test_prepare_workspace():
37+
temp_workspace = path.join(TESTING_TEMP_DIR,
38+
'.lambda_package')
39+
40+
pkg = package.Package(TESTING_TEMP_DIR)
41+
pkg.prepare_workspace()
42+
assert path.isdir(temp_workspace)
43+
assert path.isdir(path.join(temp_workspace, 'venv'))
44+
if sys.platform == 'win32' or sys.platform == 'cygwin':
45+
assert path.isfile(path.join(temp_workspace, "venv\\Scripts\\pip.exe"))
46+
else:
47+
assert path.isfile(path.join(temp_workspace, 'venv/bin/pip'))
48+
49+
50+
def test_install_requirements():
51+
reqs = ['pytest']
52+
temp_workspace = path.join(TESTING_TEMP_DIR,
53+
'.lambda_package')
54+
55+
pkg = package.Package(TESTING_TEMP_DIR)
56+
# pkg.prepare_workspace()
57+
pkg.install_requirements(reqs)
58+
site_packages = path.join(temp_workspace,
59+
'venv/lib/python2.7/site-packages')
60+
if sys.platform == 'win32' or sys.platform == 'cygwin':
61+
site_packages = path.join(temp_workspace, "venv\\lib\\site-packages")
62+
63+
assert path.isdir(path.join(site_packages, '_pytest'))
64+
65+
66+
def test_package():
67+
pkg = package.Package(TESTING_TEMP_DIR)
68+
pkg.package()
69+
assert path.isfile(path.join(TESTING_TEMP_DIR, 'lambda_function.zip'))

tox.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
norecursedirs = venv
3+
testpaths = test

0 commit comments

Comments
 (0)