mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Compare commits
3 commits
main
...
plugins-di
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33c6bcadb9 |
||
|
|
e6ac9e013c |
||
|
|
1500b37913 |
7 changed files with 96 additions and 14 deletions
|
|
@ -31,6 +31,7 @@ from .utils import (
|
||||||
get_all_foreign_keys,
|
get_all_foreign_keys,
|
||||||
is_url,
|
is_url,
|
||||||
InvalidSql,
|
InvalidSql,
|
||||||
|
module_from_path,
|
||||||
path_from_row_pks,
|
path_from_row_pks,
|
||||||
path_with_added_args,
|
path_with_added_args,
|
||||||
path_with_ext,
|
path_with_ext,
|
||||||
|
|
@ -1032,7 +1033,7 @@ class Datasette:
|
||||||
self, files, num_threads=3, cache_headers=True, page_size=100,
|
self, files, num_threads=3, cache_headers=True, page_size=100,
|
||||||
max_returned_rows=1000, sql_time_limit_ms=1000, cors=False,
|
max_returned_rows=1000, sql_time_limit_ms=1000, cors=False,
|
||||||
inspect_data=None, metadata=None, sqlite_extensions=None,
|
inspect_data=None, metadata=None, sqlite_extensions=None,
|
||||||
template_dir=None, static_mounts=None):
|
template_dir=None, plugins_dir=None, static_mounts=None):
|
||||||
self.files = files
|
self.files = files
|
||||||
self.num_threads = num_threads
|
self.num_threads = num_threads
|
||||||
self.executor = futures.ThreadPoolExecutor(
|
self.executor = futures.ThreadPoolExecutor(
|
||||||
|
|
@ -1048,7 +1049,20 @@ class Datasette:
|
||||||
self.sqlite_functions = []
|
self.sqlite_functions = []
|
||||||
self.sqlite_extensions = sqlite_extensions or []
|
self.sqlite_extensions = sqlite_extensions or []
|
||||||
self.template_dir = template_dir
|
self.template_dir = template_dir
|
||||||
|
self.plugins_dir = plugins_dir
|
||||||
self.static_mounts = static_mounts or []
|
self.static_mounts = static_mounts or []
|
||||||
|
# Execute plugins in constructor, to ensure they are available
|
||||||
|
# when the rest of `datasette inspect` executes
|
||||||
|
if self.plugins_dir:
|
||||||
|
for filename in os.listdir(self.plugins_dir):
|
||||||
|
filepath = os.path.join(self.plugins_dir, filename)
|
||||||
|
with open(filepath) as f:
|
||||||
|
mod = module_from_path(filepath, name=filename)
|
||||||
|
try:
|
||||||
|
pm.register(mod)
|
||||||
|
except ValueError:
|
||||||
|
# Plugin already registered
|
||||||
|
pass
|
||||||
|
|
||||||
def app_css_hash(self):
|
def app_css_hash(self):
|
||||||
if not hasattr(self, '_app_css_hash'):
|
if not hasattr(self, '_app_css_hash'):
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,14 @@ def inspect(files, inspect_file, sqlite_extensions):
|
||||||
@click.option('--force', is_flag=True, help='Pass --force option to now')
|
@click.option('--force', is_flag=True, help='Pass --force option to now')
|
||||||
@click.option('--branch', help='Install datasette from a GitHub branch e.g. master')
|
@click.option('--branch', help='Install datasette from a GitHub branch e.g. master')
|
||||||
@click.option('--template-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom templates')
|
@click.option('--template-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom templates')
|
||||||
|
@click.option('--plugins-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom plugins')
|
||||||
@click.option('--static', type=StaticMount(), help='mountpoint:path-to-directory for serving static files', multiple=True)
|
@click.option('--static', type=StaticMount(), help='mountpoint:path-to-directory for serving static files', multiple=True)
|
||||||
@click.option('--title', help='Title for metadata')
|
@click.option('--title', help='Title for metadata')
|
||||||
@click.option('--license', help='License label for metadata')
|
@click.option('--license', help='License label for metadata')
|
||||||
@click.option('--license_url', help='License URL for metadata')
|
@click.option('--license_url', help='License URL for metadata')
|
||||||
@click.option('--source', help='Source label for metadata')
|
@click.option('--source', help='Source label for metadata')
|
||||||
@click.option('--source_url', help='Source URL for metadata')
|
@click.option('--source_url', help='Source URL for metadata')
|
||||||
def publish(publisher, files, name, metadata, extra_options, force, branch, template_dir, static, **extra_metadata):
|
def publish(publisher, files, name, metadata, extra_options, force, branch, template_dir, plugins_dir, static, **extra_metadata):
|
||||||
"""
|
"""
|
||||||
Publish specified SQLite database files to the internet along with a datasette API.
|
Publish specified SQLite database files to the internet along with a datasette API.
|
||||||
|
|
||||||
|
|
@ -94,7 +95,7 @@ def publish(publisher, files, name, metadata, extra_options, force, branch, temp
|
||||||
|
|
||||||
if publisher == 'now':
|
if publisher == 'now':
|
||||||
_fail_if_publish_binary_not_installed('now', 'Zeit Now', 'https://zeit.co/now')
|
_fail_if_publish_binary_not_installed('now', 'Zeit Now', 'https://zeit.co/now')
|
||||||
with temporary_docker_directory(files, name, metadata, extra_options, branch, template_dir, static, extra_metadata):
|
with temporary_docker_directory(files, name, metadata, extra_options, branch, template_dir, plugins_dir, static, extra_metadata):
|
||||||
if force:
|
if force:
|
||||||
call(['now', '--force'])
|
call(['now', '--force'])
|
||||||
else:
|
else:
|
||||||
|
|
@ -110,7 +111,7 @@ def publish(publisher, files, name, metadata, extra_options, force, branch, temp
|
||||||
click.confirm('Install it? (this will run `heroku plugins:install heroku-builds`)', abort=True)
|
click.confirm('Install it? (this will run `heroku plugins:install heroku-builds`)', abort=True)
|
||||||
call(["heroku", "plugins:install", "heroku-builds"])
|
call(["heroku", "plugins:install", "heroku-builds"])
|
||||||
|
|
||||||
with temporary_heroku_directory(files, name, metadata, extra_options, branch, template_dir, static, extra_metadata):
|
with temporary_heroku_directory(files, name, metadata, extra_options, branch, template_dir, plugins_dir, static, extra_metadata):
|
||||||
create_output = check_output(
|
create_output = check_output(
|
||||||
['heroku', 'apps:create', '--json']
|
['heroku', 'apps:create', '--json']
|
||||||
).decode('utf8')
|
).decode('utf8')
|
||||||
|
|
@ -190,13 +191,14 @@ def skeleton(files, metadata, sqlite_extensions):
|
||||||
@click.option('--extra-options', help='Extra options to pass to datasette serve')
|
@click.option('--extra-options', help='Extra options to pass to datasette serve')
|
||||||
@click.option('--branch', help='Install datasette from a GitHub branch e.g. master')
|
@click.option('--branch', help='Install datasette from a GitHub branch e.g. master')
|
||||||
@click.option('--template-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom templates')
|
@click.option('--template-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom templates')
|
||||||
|
@click.option('--plugins-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom plugins')
|
||||||
@click.option('--static', type=StaticMount(), help='mountpoint:path-to-directory for serving static files', multiple=True)
|
@click.option('--static', type=StaticMount(), help='mountpoint:path-to-directory for serving static files', multiple=True)
|
||||||
@click.option('--title', help='Title for metadata')
|
@click.option('--title', help='Title for metadata')
|
||||||
@click.option('--license', help='License label for metadata')
|
@click.option('--license', help='License label for metadata')
|
||||||
@click.option('--license_url', help='License URL for metadata')
|
@click.option('--license_url', help='License URL for metadata')
|
||||||
@click.option('--source', help='Source label for metadata')
|
@click.option('--source', help='Source label for metadata')
|
||||||
@click.option('--source_url', help='Source URL for metadata')
|
@click.option('--source_url', help='Source URL for metadata')
|
||||||
def package(files, tag, metadata, extra_options, branch, template_dir, static, **extra_metadata):
|
def package(files, tag, metadata, extra_options, branch, template_dir, plugins_dir, static, **extra_metadata):
|
||||||
"Package specified SQLite files into a new datasette Docker container"
|
"Package specified SQLite files into a new datasette Docker container"
|
||||||
if not shutil.which('docker'):
|
if not shutil.which('docker'):
|
||||||
click.secho(
|
click.secho(
|
||||||
|
|
@ -207,7 +209,7 @@ def package(files, tag, metadata, extra_options, branch, template_dir, static, *
|
||||||
err=True,
|
err=True,
|
||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
with temporary_docker_directory(files, 'datasette', metadata, extra_options, branch, template_dir, static, extra_metadata):
|
with temporary_docker_directory(files, 'datasette', metadata, extra_options, branch, template_dir, plugins_dir, static, extra_metadata):
|
||||||
args = ['docker', 'build']
|
args = ['docker', 'build']
|
||||||
if tag:
|
if tag:
|
||||||
args.append('-t')
|
args.append('-t')
|
||||||
|
|
@ -233,8 +235,9 @@ def package(files, tag, metadata, extra_options, branch, template_dir, static, *
|
||||||
@click.option('--inspect-file', help='Path to JSON file created using "datasette inspect"')
|
@click.option('--inspect-file', help='Path to JSON file created using "datasette inspect"')
|
||||||
@click.option('-m', '--metadata', type=click.File(mode='r'), help='Path to JSON file containing license/source metadata')
|
@click.option('-m', '--metadata', type=click.File(mode='r'), help='Path to JSON file containing license/source metadata')
|
||||||
@click.option('--template-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom templates')
|
@click.option('--template-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom templates')
|
||||||
|
@click.option('--plugins-dir', type=click.Path(exists=True, file_okay=False, dir_okay=True), help='Path to directory containing custom plugins')
|
||||||
@click.option('--static', type=StaticMount(), help='mountpoint:path-to-directory for serving static files', multiple=True)
|
@click.option('--static', type=StaticMount(), help='mountpoint:path-to-directory for serving static files', multiple=True)
|
||||||
def serve(files, host, port, debug, reload, cors, page_size, max_returned_rows, sql_time_limit_ms, sqlite_extensions, inspect_file, metadata, template_dir, static):
|
def serve(files, host, port, debug, reload, cors, page_size, max_returned_rows, sql_time_limit_ms, sqlite_extensions, inspect_file, metadata, template_dir, plugins_dir, static):
|
||||||
"""Serve up specified SQLite database files with a web UI"""
|
"""Serve up specified SQLite database files with a web UI"""
|
||||||
if reload:
|
if reload:
|
||||||
import hupper
|
import hupper
|
||||||
|
|
@ -262,6 +265,7 @@ def serve(files, host, port, debug, reload, cors, page_size, max_returned_rows,
|
||||||
metadata=metadata_data,
|
metadata=metadata_data,
|
||||||
sqlite_extensions=sqlite_extensions,
|
sqlite_extensions=sqlite_extensions,
|
||||||
template_dir=template_dir,
|
template_dir=template_dir,
|
||||||
|
plugins_dir=plugins_dir,
|
||||||
static_mounts=static,
|
static_mounts=static,
|
||||||
)
|
)
|
||||||
# Force initial hashing/table counting
|
# Force initial hashing/table counting
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import imp
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -182,7 +183,7 @@ def escape_sqlite(s):
|
||||||
return '[{}]'.format(s)
|
return '[{}]'.format(s)
|
||||||
|
|
||||||
|
|
||||||
def make_dockerfile(files, metadata_file, extra_options, branch, template_dir, static):
|
def make_dockerfile(files, metadata_file, extra_options, branch, template_dir, plugins_dir, static):
|
||||||
cmd = ['"datasette"', '"serve"', '"--host"', '"0.0.0.0"']
|
cmd = ['"datasette"', '"serve"', '"--host"', '"0.0.0.0"']
|
||||||
cmd.append('"' + '", "'.join(files) + '"')
|
cmd.append('"' + '", "'.join(files) + '"')
|
||||||
cmd.extend(['"--cors"', '"--port"', '"8001"', '"--inspect-file"', '"inspect-data.json"'])
|
cmd.extend(['"--cors"', '"--port"', '"8001"', '"--inspect-file"', '"inspect-data.json"'])
|
||||||
|
|
@ -190,6 +191,8 @@ def make_dockerfile(files, metadata_file, extra_options, branch, template_dir, s
|
||||||
cmd.extend(['"--metadata"', '"{}"'.format(metadata_file)])
|
cmd.extend(['"--metadata"', '"{}"'.format(metadata_file)])
|
||||||
if template_dir:
|
if template_dir:
|
||||||
cmd.extend(['"--template-dir"', '"templates/"'])
|
cmd.extend(['"--template-dir"', '"templates/"'])
|
||||||
|
if plugins_dir:
|
||||||
|
cmd.extend(['"--plugins-dir"', '"plugins/"'])
|
||||||
if static:
|
if static:
|
||||||
for mount_point, _ in static:
|
for mount_point, _ in static:
|
||||||
cmd.extend(['"--static"', '"{}:{}"'.format(mount_point, mount_point)])
|
cmd.extend(['"--static"', '"{}:{}"'.format(mount_point, mount_point)])
|
||||||
|
|
@ -216,7 +219,7 @@ CMD [{cmd}]'''.format(
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def temporary_docker_directory(files, name, metadata, extra_options, branch, template_dir, static, extra_metadata=None):
|
def temporary_docker_directory(files, name, metadata, extra_options, branch, template_dir, plugins_dir, static, extra_metadata=None):
|
||||||
extra_metadata = extra_metadata or {}
|
extra_metadata = extra_metadata or {}
|
||||||
tmp = tempfile.TemporaryDirectory()
|
tmp = tempfile.TemporaryDirectory()
|
||||||
# We create a datasette folder in there to get a nicer now deploy name
|
# We create a datasette folder in there to get a nicer now deploy name
|
||||||
|
|
@ -242,6 +245,7 @@ def temporary_docker_directory(files, name, metadata, extra_options, branch, tem
|
||||||
extra_options,
|
extra_options,
|
||||||
branch,
|
branch,
|
||||||
template_dir,
|
template_dir,
|
||||||
|
plugins_dir,
|
||||||
static,
|
static,
|
||||||
)
|
)
|
||||||
os.chdir(datasette_dir)
|
os.chdir(datasette_dir)
|
||||||
|
|
@ -255,6 +259,11 @@ def temporary_docker_directory(files, name, metadata, extra_options, branch, tem
|
||||||
os.path.join(saved_cwd, template_dir),
|
os.path.join(saved_cwd, template_dir),
|
||||||
os.path.join(datasette_dir, 'templates')
|
os.path.join(datasette_dir, 'templates')
|
||||||
)
|
)
|
||||||
|
if plugins_dir:
|
||||||
|
link_or_copy_directory(
|
||||||
|
os.path.join(saved_cwd, plugins_dir),
|
||||||
|
os.path.join(datasette_dir, 'plugins')
|
||||||
|
)
|
||||||
for mount_point, path in static:
|
for mount_point, path in static:
|
||||||
link_or_copy_directory(
|
link_or_copy_directory(
|
||||||
os.path.join(saved_cwd, path),
|
os.path.join(saved_cwd, path),
|
||||||
|
|
@ -267,7 +276,7 @@ def temporary_docker_directory(files, name, metadata, extra_options, branch, tem
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def temporary_heroku_directory(files, name, metadata, extra_options, branch, template_dir, static, extra_metadata=None):
|
def temporary_heroku_directory(files, name, metadata, extra_options, branch, template_dir, plugins_dir, static, extra_metadata=None):
|
||||||
# FIXME: lots of duplicated code from above
|
# FIXME: lots of duplicated code from above
|
||||||
|
|
||||||
extra_metadata = extra_metadata or {}
|
extra_metadata = extra_metadata or {}
|
||||||
|
|
@ -314,6 +323,13 @@ def temporary_heroku_directory(files, name, metadata, extra_options, branch, tem
|
||||||
os.path.join(tmp.name, 'templates')
|
os.path.join(tmp.name, 'templates')
|
||||||
)
|
)
|
||||||
extras.extend(['--template-dir', 'templates/'])
|
extras.extend(['--template-dir', 'templates/'])
|
||||||
|
if plugins_dir:
|
||||||
|
link_or_copy_directory(
|
||||||
|
os.path.join(saved_cwd, plugins_dir),
|
||||||
|
os.path.join(tmp.name, 'plugins')
|
||||||
|
)
|
||||||
|
extras.extend(['--plugins-dir', 'plugins/'])
|
||||||
|
|
||||||
if metadata:
|
if metadata:
|
||||||
extras.extend(['--metadata', 'metadata.json'])
|
extras.extend(['--metadata', 'metadata.json'])
|
||||||
for mount_point, path in static:
|
for mount_point, path in static:
|
||||||
|
|
@ -625,3 +641,13 @@ def link_or_copy_directory(src, dst):
|
||||||
shutil.copytree(src, dst, copy_function=os.link)
|
shutil.copytree(src, dst, copy_function=os.link)
|
||||||
except OSError:
|
except OSError:
|
||||||
shutil.copytree(src, dst)
|
shutil.copytree(src, dst)
|
||||||
|
|
||||||
|
|
||||||
|
def module_from_path(path, name):
|
||||||
|
# Adapted from http://sayspy.blogspot.com/2011/07/how-to-import-module-from-just-file.html
|
||||||
|
mod = imp.new_module(name)
|
||||||
|
mod.__file__ = path
|
||||||
|
with open(path, 'r') as file:
|
||||||
|
code = compile(file.read(), path, 'exec', dont_inherit=True)
|
||||||
|
exec(code, mod.__dict__)
|
||||||
|
return mod
|
||||||
|
|
|
||||||
12
setup.py
12
setup.py
|
|
@ -1,5 +1,4 @@
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
from datasette.version import __version__
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,13 +9,22 @@ def get_long_description():
|
||||||
return fp.read()
|
return fp.read()
|
||||||
|
|
||||||
|
|
||||||
|
def get_version():
|
||||||
|
path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), 'datasette', 'version.py'
|
||||||
|
)
|
||||||
|
g = {}
|
||||||
|
exec(open(path).read(), g)
|
||||||
|
return g['__version__']
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='datasette',
|
name='datasette',
|
||||||
description='An instant JSON API for your SQLite databases',
|
description='An instant JSON API for your SQLite databases',
|
||||||
long_description=get_long_description(),
|
long_description=get_long_description(),
|
||||||
long_description_content_type='text/markdown',
|
long_description_content_type='text/markdown',
|
||||||
author='Simon Willison',
|
author='Simon Willison',
|
||||||
version=__version__,
|
version=get_version(),
|
||||||
license='Apache License, Version 2.0',
|
license='Apache License, Version 2.0',
|
||||||
url='https://github.com/simonw/datasette',
|
url='https://github.com/simonw/datasette',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,16 @@ def app_client():
|
||||||
conn = sqlite3.connect(filepath)
|
conn = sqlite3.connect(filepath)
|
||||||
conn.executescript(TABLES)
|
conn.executescript(TABLES)
|
||||||
os.chdir(os.path.dirname(filepath))
|
os.chdir(os.path.dirname(filepath))
|
||||||
|
plugins_dir = os.path.join(tmpdir, 'plugins')
|
||||||
|
os.mkdir(plugins_dir)
|
||||||
|
open(os.path.join(plugins_dir, 'my_plugin.py'), 'w').write(PLUGIN)
|
||||||
ds = Datasette(
|
ds = Datasette(
|
||||||
[filepath],
|
[filepath],
|
||||||
page_size=50,
|
page_size=50,
|
||||||
max_returned_rows=100,
|
max_returned_rows=100,
|
||||||
sql_time_limit_ms=20,
|
sql_time_limit_ms=20,
|
||||||
metadata=METADATA,
|
metadata=METADATA,
|
||||||
|
plugins_dir=plugins_dir,
|
||||||
)
|
)
|
||||||
ds.sqlite_functions.append(
|
ds.sqlite_functions.append(
|
||||||
('sleep', 1, lambda n: time.sleep(float(n))),
|
('sleep', 1, lambda n: time.sleep(float(n))),
|
||||||
|
|
@ -90,6 +94,20 @@ METADATA = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLUGIN = '''
|
||||||
|
from datasette import hookimpl
|
||||||
|
import pint
|
||||||
|
|
||||||
|
ureg = pint.UnitRegistry()
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl
|
||||||
|
def prepare_connection(conn):
|
||||||
|
def convert_units(amount, from_, to_):
|
||||||
|
"select convert_units(100, 'm', 'ft');"
|
||||||
|
return (amount * ureg(from_)).to(to_).to_tuple()[0]
|
||||||
|
conn.create_function('convert_units', 3, convert_units)
|
||||||
|
'''
|
||||||
|
|
||||||
TABLES = '''
|
TABLES = '''
|
||||||
CREATE TABLE simple_primary_key (
|
CREATE TABLE simple_primary_key (
|
||||||
|
|
|
||||||
|
|
@ -588,8 +588,10 @@ def test_row_foreign_key_tables(app_client):
|
||||||
|
|
||||||
|
|
||||||
def test_unit_filters(app_client):
|
def test_unit_filters(app_client):
|
||||||
response = app_client.get('/test_tables/units.json?distance__lt=75km&frequency__gt=1kHz',
|
response = app_client.get(
|
||||||
gather_request=False)
|
'/test_tables/units.json?distance__lt=75km&frequency__gt=1kHz',
|
||||||
|
gather_request=False
|
||||||
|
)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
data = response.json
|
data = response.json
|
||||||
|
|
||||||
|
|
@ -598,3 +600,11 @@ def test_unit_filters(app_client):
|
||||||
|
|
||||||
assert len(data['rows']) == 1
|
assert len(data['rows']) == 1
|
||||||
assert data['rows'][0][0] == 2
|
assert data['rows'][0][0] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugins_dir_plugin(app_client):
|
||||||
|
response = app_client.get(
|
||||||
|
"/test_tables.json?sql=select+convert_units(100%2C+'m'%2C+'ft')",
|
||||||
|
gather_request=False
|
||||||
|
)
|
||||||
|
assert pytest.approx(328.0839) == response.json['rows'][0][0]
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,7 @@ def test_temporary_docker_directory_uses_hard_link():
|
||||||
extra_options=None,
|
extra_options=None,
|
||||||
branch=None,
|
branch=None,
|
||||||
template_dir=None,
|
template_dir=None,
|
||||||
|
plugins_dir=None,
|
||||||
static=[],
|
static=[],
|
||||||
) as temp_docker:
|
) as temp_docker:
|
||||||
hello = os.path.join(temp_docker, 'hello')
|
hello = os.path.join(temp_docker, 'hello')
|
||||||
|
|
@ -218,6 +219,7 @@ def test_temporary_docker_directory_uses_copy_if_hard_link_fails(mock_link):
|
||||||
extra_options=None,
|
extra_options=None,
|
||||||
branch=None,
|
branch=None,
|
||||||
template_dir=None,
|
template_dir=None,
|
||||||
|
plugins_dir=None,
|
||||||
static=[],
|
static=[],
|
||||||
) as temp_docker:
|
) as temp_docker:
|
||||||
hello = os.path.join(temp_docker, 'hello')
|
hello = os.path.join(temp_docker, 'hello')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue