diff --git a/compiler/cli.py b/compiler/cli.py
index d1fcb59..a721c13 100644
--- a/compiler/cli.py
+++ b/compiler/cli.py
@@ -92,7 +92,13 @@ def make_command_line_parser():
help='Path to add to sys.path, may be repeated to provide multiple roots.',
action='append',
default=[],
- dest='import_roots')
+ dest='import_roots'),
+ parser.add_argument(
+ '--no_remove',
+ help='Keep extracted files after program finishes if --zip_safe is ' +
+ 'enabled.',
+ type=bool_from_string,
+ required=True)
return parser
@@ -170,5 +176,6 @@ def main(argv):
manifest_root=args.manifest_root,
timestamp=args.timestamp,
zip_safe=args.zip_safe,
+ no_remove=args.no_remove,
)
par.create()
diff --git a/compiler/cli_test.py b/compiler/cli_test.py
index 4bdaf34..bd375e6 100644
--- a/compiler/cli_test.py
+++ b/compiler/cli_test.py
@@ -38,6 +38,7 @@ def test_make_command_line_parser(self):
'--output_par=baz',
'--stub_file=quux',
'--zip_safe=False',
+ '--no_remove=False',
'--import_root=root1',
'--import_root=root2',
'foo',
@@ -58,6 +59,7 @@ def test_make_command_line_parser_for_interprerter(self):
'--output_par=baz',
'--stub_file=quux',
'--zip_safe=False',
+ '--no_remove=False',
'--interpreter=foobar',
'foo',
])
diff --git a/compiler/python_archive.py b/compiler/python_archive.py
index 2c69c58..a4a9093 100755
--- a/compiler/python_archive.py
+++ b/compiler/python_archive.py
@@ -48,7 +48,8 @@
_boilerplate_template = """\
# Boilerplate added by subpar/compiler/python_archive.py
from %(runtime_package)s import support as _
-_.setup(import_roots=%(import_roots)s, zip_safe=%(zip_safe)s)
+_.setup(import_roots=%(import_roots)s, zip_safe=%(zip_safe)s,
+ no_remove=%(no_remove)s)
del _
# End boilerplate
"""
@@ -102,6 +103,7 @@ def __init__(self,
output_filename,
timestamp,
zip_safe,
+ no_remove,
):
self.main_filename = main_filename
@@ -114,6 +116,7 @@ def __init__(self,
t = datetime.utcfromtimestamp(timestamp)
self.timestamp_tuple = t.timetuple()[0:6]
self.zip_safe = zip_safe
+ self.no_remove = no_remove
self.compression = zipfile.ZIP_DEFLATED
@@ -172,6 +175,7 @@ def generate_boilerplate(self, import_roots):
'runtime_package': _runtime_package,
'import_roots': str(import_roots),
'zip_safe': self.zip_safe,
+ 'no_remove': self.no_remove,
}
return boilerplate_contents.encode('ascii').decode('ascii')
diff --git a/compiler/python_archive_test.py b/compiler/python_archive_test.py
index b918399..05400d7 100644
--- a/compiler/python_archive_test.py
+++ b/compiler/python_archive_test.py
@@ -51,6 +51,7 @@ def setUp(self):
self.date_time_tuple = (1980, 1, 1, 0, 0, 0)
self.timestamp = 315532800
self.zip_safe = True
+ self.no_remove = False
def _construct(self, manifest_filename=None):
return python_archive.PythonArchive(
@@ -62,6 +63,7 @@ def _construct(self, manifest_filename=None):
output_filename=self.output_filename,
timestamp=self.timestamp,
zip_safe=self.zip_safe,
+ no_remove=self.no_remove,
)
def test_create_manifest_not_found(self):
diff --git a/docs/subpar.html b/docs/subpar.html
index d1fc5d1..d510172 100644
--- a/docs/subpar.html
+++ b/docs/subpar.html
@@ -154,7 +154,7 @@
Attributes
parfile
- parfile(name, src, compiler, compiler_args, default_python_version, imports, main, zip_safe)
+ parfile(name, src, compiler, compiler_args, default_python_version, imports, main, no_remove, zip_safe)
A self-contained, single-file Python program, with a .par file extension.
You probably want to use par_binary() instead of this.
@@ -223,6 +223,14 @@ Attributes
See py_binary.main
+
+ no_remove |
+
+ Boolean; Optional; Default is False
+ Whether to keep the extracted temporary directory after the
+program finishes, if zip_safe is enabled.
+ |
+
zip_safe |
@@ -239,7 +247,7 @@ Attributes
parfile_test
- parfile_test(name, src, compiler, compiler_args, default_python_version, imports, main, zip_safe)
+ parfile_test(name, src, compiler, compiler_args, default_python_version, imports, main, no_remove, zip_safe)
Identical to par_binary, but the rule is marked as being a test.
You probably want to use par_test() instead of this.
@@ -306,6 +314,14 @@ Attributes
See py_binary.main
|
+
+ no_remove |
+
+ Boolean; Optional; Default is False
+ Whether to keep the extracted temporary directory after the
+program finishes, if zip_safe is enabled.
+ |
+
zip_safe |
diff --git a/docs/subpar.md b/docs/subpar.md
index aaf19ab..ddde9df 100644
--- a/docs/subpar.md
+++ b/docs/subpar.md
@@ -108,7 +108,7 @@ specifically need to test a module's behaviour when used in a .par binary.
## parfile
-parfile(name, src, compiler, compiler_args, default_python_version, imports, main, zip_safe)
+parfile(name, src, compiler, compiler_args, default_python_version, imports, main, no_remove, zip_safe)
A self-contained, single-file Python program, with a .par file extension.
@@ -182,6 +182,14 @@ the application.
See py_binary.main
|
+
+ no_remove |
+
+ Boolean; Optional; Default is False
+ Whether to keep the extracted temporary directory after the
+program finishes, if zip_safe is enabled.
+ |
+
zip_safe |
@@ -198,7 +206,7 @@ par file executes.
## parfile_test
-parfile_test(name, src, compiler, compiler_args, default_python_version, imports, main, zip_safe)
+parfile_test(name, src, compiler, compiler_args, default_python_version, imports, main, no_remove, zip_safe)
Identical to par_binary, but the rule is marked as being a test.
@@ -269,6 +277,14 @@ the application.
See py_binary.main
|
+
+ no_remove |
+
+ Boolean; Optional; Default is False
+ Whether to keep the extracted temporary directory after the
+program finishes, if zip_safe is enabled.
+ |
+
zip_safe |
diff --git a/runtime/support.py b/runtime/support.py
index e8ec253..b5bcb84 100644
--- a/runtime/support.py
+++ b/runtime/support.py
@@ -95,7 +95,7 @@ def _find_archive():
return archive_path
-def _extract_files(archive_path):
+def _extract_files(archive_path, no_remove):
"""Extract the contents of this .par file to disk.
This creates a temporary directory, and registers an atexit
@@ -107,9 +107,10 @@ def _extract_files(archive_path):
"""
extract_dir = tempfile.mkdtemp()
- def _extract_files_cleanup():
- shutil.rmtree(extract_dir, ignore_errors=True)
- atexit.register(_extract_files_cleanup)
+ if not no_remove:
+ def _extract_files_cleanup():
+ shutil.rmtree(extract_dir, ignore_errors=True)
+ atexit.register(_extract_files_cleanup)
_log('# extracting %s to %s' % (archive_path, extract_dir))
zip_file = zipfile.ZipFile(archive_path, mode='r')
@@ -282,7 +283,7 @@ def _initialize_import_path(import_roots, import_prefix):
_log('# adding %s to sys.path' % full_roots)
-def setup(import_roots, zip_safe):
+def setup(import_roots, zip_safe, no_remove):
"""Initialize subpar run-time support
Args:
@@ -309,7 +310,7 @@ def setup(import_roots, zip_safe):
# Extract files to disk if necessary
if not zip_safe:
- extract_dir = _extract_files(archive_path)
+ extract_dir = _extract_files(archive_path, no_remove)
# sys.path[0] is the name of the executing .par file. Point
# it to the extract directory instead, so that Python searches
# there for imports.
diff --git a/runtime/support_test.py b/runtime/support_test.py
index 565adb1..8dda700 100644
--- a/runtime/support_test.py
+++ b/runtime/support_test.py
@@ -101,7 +101,7 @@ def test__find_archive(self):
def test__extract_files(self):
# Extract zipfile
- extract_path = support._extract_files(self.zipfile_name)
+ extract_path = support._extract_files(self.zipfile_name, False)
# Check results
self.assertTrue(os.path.isdir(extract_path))
@@ -140,7 +140,7 @@ def test_setup(self):
mock_sys_path[0] = self.zipfile_name
sys.path = mock_sys_path
success = support.setup(import_roots=['some_root', 'another_root'],
- zip_safe=True)
+ zip_safe=True, no_remove=False)
self.assertTrue(success)
finally:
sys.path = old_sys_path
@@ -165,7 +165,8 @@ def test_setup__extract(self):
mock_sys_path = list(sys.path)
mock_sys_path[0] = self.zipfile_name
sys.path = mock_sys_path
- success = support.setup(import_roots=['some_root'], zip_safe=False)
+ success = support.setup(import_roots=['some_root'], zip_safe=False,
+ no_remove=False)
self.assertTrue(success)
finally:
sys.path = old_sys_path
diff --git a/subpar.bzl b/subpar.bzl
index ae685bf..11e3b18 100644
--- a/subpar.bzl
+++ b/subpar.bzl
@@ -73,6 +73,7 @@ def _parfile_impl(ctx):
]
zip_safe = ctx.attr.zip_safe
+ no_remove = ctx.attr.no_remove
# Assemble command line for .par compiler
args = ctx.attr.compiler_args + [
@@ -84,6 +85,8 @@ def _parfile_impl(ctx):
ctx.attr.src.files_to_run.executable.path,
"--zip_safe",
str(zip_safe),
+ '--no_remove',
+ str(no_remove),
]
for import_root in import_roots:
args.extend(['--import_root', import_root])
@@ -136,6 +139,7 @@ parfile_attrs = {
),
"compiler_args": attr.string_list(default = []),
"zip_safe": attr.bool(default = True),
+ "no_remove": attr.bool(default = False),
}
# Rule to create a parfile given a py_binary() as input
@@ -171,6 +175,9 @@ Args:
extracted to a temporary directory on disk each time the
par file executes.
+ no_remove: Whether to keep the extracted temporary directory after the
+ program finishes, if zip_safe is enabled.
+
TODO(b/27502830): A directory foo.par.runfiles is also created. This
is a bug, don't use or depend on it.
"""
@@ -204,6 +211,7 @@ def par_binary(name, **kwargs):
compiler = kwargs.pop("compiler", None)
compiler_args = kwargs.pop("compiler_args", [])
zip_safe = kwargs.pop("zip_safe", True)
+ no_remove = kwargs.pop('no_remove', False)
native.py_binary(name = name, **kwargs)
main = kwargs.get("main", name + ".py")
@@ -222,6 +230,7 @@ def par_binary(name, **kwargs):
testonly = testonly,
visibility = visibility,
zip_safe = zip_safe,
+ no_remove=no_remove,
)
def par_test(name, **kwargs):
@@ -232,6 +241,7 @@ def par_test(name, **kwargs):
"""
compiler = kwargs.pop("compiler", None)
zip_safe = kwargs.pop("zip_safe", True)
+ no_remove = kwargs.pop('no_remove', False)
native.py_test(name = name, **kwargs)
main = kwargs.get("main", name + ".py")
@@ -249,4 +259,5 @@ def par_test(name, **kwargs):
testonly = testonly,
visibility = visibility,
zip_safe = zip_safe,
+ no_remove=no_remove,
)
diff --git a/tests/BUILD b/tests/BUILD
index f38083b..00030fd 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -90,6 +90,7 @@ par_binary(
main = "package_extract/extract.py",
srcs_version = "PY2AND3",
zip_safe = False,
+ no_remove = False,
)
par_binary(
|