diff --git a/.isort.cfg b/.isort.cfg index 0cece53b..e3955e56 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,3 +1,7 @@ [settings] multi_line_output=3 - +include_trailing_comma=True +force_grid_wrap=0 +use_parentheses=True +line_length=88 +known_first_party=datasette diff --git a/datasette/__init__.py b/datasette/__init__.py index 271e09ad..8f57d6da 100644 --- a/datasette/__init__.py +++ b/datasette/__init__.py @@ -1,7 +1,8 @@ from datasette.permissions import Permission # noqa -from datasette.version import __version_info__, __version__ # noqa -from datasette.utils.asgi import Forbidden, NotFound, Request, Response # noqa from datasette.utils import actor_matches_allow # noqa +from datasette.utils.asgi import Forbidden, NotFound, Request, Response # noqa +from datasette.version import __version__, __version_info__ # noqa from datasette.views import Context # noqa + from .hookspecs import hookimpl # noqa from .hookspecs import hookspec # noqa diff --git a/datasette/actor_auth_cookie.py b/datasette/actor_auth_cookie.py index 368213af..7503f1d5 100644 --- a/datasette/actor_auth_cookie.py +++ b/datasette/actor_auth_cookie.py @@ -1,8 +1,10 @@ -from datasette import hookimpl -from itsdangerous import BadSignature -from datasette.utils import baseconv import time +from itsdangerous import BadSignature + +from datasette import hookimpl +from datasette.utils import baseconv + @hookimpl def actor_from_request(datasette, request): diff --git a/datasette/app.py b/datasette/app.py index 1871aeb1..149caccc 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -1,18 +1,13 @@ import asyncio -from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union -import asgi_csrf import collections import dataclasses import datetime import functools import glob import hashlib -import httpx import inspect -from itsdangerous import BadSignature import json import os -import pkg_resources import re import secrets import sys @@ -22,47 +17,25 @@ import types import urllib.parse from concurrent import futures from pathlib import Path +from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, Union -from markupsafe import Markup, escape -from itsdangerous import URLSafeSerializer -from jinja2 import ( - ChoiceLoader, - Environment, - FileSystemLoader, - PrefixLoader, -) +import asgi_csrf +import httpx +import pkg_resources +from itsdangerous import BadSignature, URLSafeSerializer +from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader from jinja2.environment import Template from jinja2.exceptions import TemplateNotFound +from markupsafe import Markup, escape -from .views import Context -from .views.base import ureg -from .views.database import database_download, DatabaseView, TableCreateView -from .views.index import IndexView -from .views.special import ( - JsonDataView, - PatternPortfolioView, - AuthTokenView, - ApiExplorerView, - CreateTokenView, - LogoutView, - AllowDebugView, - PermissionsDebugView, - MessagesDebugView, -) -from .views.table import ( - TableInsertView, - TableUpsertView, - TableDropView, - table_view, -) -from .views.row import RowView, RowDeleteView, RowUpdateView -from .renderer import json_renderer -from .url_builder import Urls from .database import Database, QueryInterrupted - +from .plugins import DEFAULT_PLUGINS, get_plugins, pm +from .renderer import json_renderer +from .tracer import AsgiTracer +from .url_builder import Urls from .utils import ( - PrefixedUrlString, SPATIALITE_FUNCTIONS, + PrefixedUrlString, StartupError, async_call_with_supported_arguments, await_me_maybe, @@ -76,34 +49,46 @@ from .utils import ( parse_metadata, resolve_env_secrets, resolve_routes, + row_sql_params_pks, tilde_decode, to_css_class, urlsafe_components, - row_sql_params_pks, ) from .utils.asgi import ( AsgiLifespan, + AsgiRunOnFirstRequest, + DatabaseNotFound, Forbidden, NotFound, - DatabaseNotFound, - TableNotFound, - RowNotFound, Request, Response, - AsgiRunOnFirstRequest, - asgi_static, + RowNotFound, + TableNotFound, asgi_send, asgi_send_file, asgi_send_redirect, + asgi_static, ) from .utils.internal_db import init_internal_db, populate_schema_tables -from .utils.sqlite import ( - sqlite3, - using_pysqlite3, -) -from .tracer import AsgiTracer -from .plugins import pm, DEFAULT_PLUGINS, get_plugins +from .utils.sqlite import sqlite3, using_pysqlite3 from .version import __version__ +from .views import Context +from .views.base import ureg +from .views.database import DatabaseView, TableCreateView, database_download +from .views.index import IndexView +from .views.row import RowDeleteView, RowUpdateView, RowView +from .views.special import ( + AllowDebugView, + ApiExplorerView, + AuthTokenView, + CreateTokenView, + JsonDataView, + LogoutView, + MessagesDebugView, + PatternPortfolioView, + PermissionsDebugView, +) +from .views.table import TableDropView, TableInsertView, TableUpsertView, table_view app_root = Path(__file__).parent.parent diff --git a/datasette/blob_renderer.py b/datasette/blob_renderer.py index 4d8c6bea..b6c8b77f 100644 --- a/datasette/blob_renderer.py +++ b/datasette/blob_renderer.py @@ -1,8 +1,9 @@ -from datasette import hookimpl -from datasette.utils.asgi import Response, BadRequest -from datasette.utils import to_css_class import hashlib +from datasette import hookimpl +from datasette.utils import to_css_class +from datasette.utils.asgi import BadRequest, Response + _BLOB_COLUMN = "_blob_column" _BLOB_HASH = "_blob_hash" diff --git a/datasette/cli.py b/datasette/cli.py index dbbfaba7..1ebfb044 100644 --- a/datasette/cli.py +++ b/datasette/cli.py @@ -1,41 +1,43 @@ import asyncio -import uvicorn -import click -from click import formatting -from click.types import CompositeParamType -from click_default_group import DefaultGroup import functools import json import os import pathlib -from runpy import run_module import shutil -from subprocess import call import sys import textwrap import webbrowser +from runpy import run_module +from subprocess import call + +import click +import uvicorn +from click import formatting +from click.types import CompositeParamType +from click_default_group import DefaultGroup + from .app import ( - OBSOLETE_SETTINGS, - Datasette, DEFAULT_SETTINGS, + OBSOLETE_SETTINGS, SETTINGS, SQLITE_LIMIT_ATTACHED, + Datasette, pm, ) from .utils import ( - LoadExtension, - StartupError, - check_connection, - find_spatialite, - parse_metadata, ConnectionProblem, + LoadExtension, SpatialiteConnectionProblem, - initial_path_for_datasette, - temporary_docker_directory, - value_as_boolean, SpatialiteNotFound, + StartupError, StaticMount, ValueAsBooleanError, + check_connection, + find_spatialite, + initial_path_for_datasette, + parse_metadata, + temporary_docker_directory, + value_as_boolean, ) from .utils.sqlite import sqlite3 from .utils.testing import TestClient diff --git a/datasette/database.py b/datasette/database.py index af39ac9e..bd17427d 100644 --- a/datasette/database.py +++ b/datasette/database.py @@ -1,13 +1,15 @@ import asyncio -from collections import namedtuple -from pathlib import Path import hashlib -import janus import queue import sys import threading import uuid +from collections import namedtuple +from pathlib import Path +import janus + +from .inspect import inspect_hash from .tracer import trace from .utils import ( detect_fts, @@ -15,12 +17,11 @@ from .utils import ( detect_spatialite, get_all_foreign_keys, get_outbound_foreign_keys, - sqlite_timelimit, sqlite3, - table_columns, + sqlite_timelimit, table_column_details, + table_columns, ) -from .inspect import inspect_hash connections = threading.local() diff --git a/datasette/default_magic_parameters.py b/datasette/default_magic_parameters.py index 19382207..edce6f3d 100644 --- a/datasette/default_magic_parameters.py +++ b/datasette/default_magic_parameters.py @@ -1,8 +1,9 @@ -from datasette import hookimpl import datetime import os import time +from datasette import hookimpl + def header(key, request): key = key.replace("_", "-").encode("utf-8") diff --git a/datasette/default_permissions.py b/datasette/default_permissions.py index 63a66c3c..a6a667bc 100644 --- a/datasette/default_permissions.py +++ b/datasette/default_permissions.py @@ -1,8 +1,10 @@ -from datasette import hookimpl, Permission -from datasette.utils import actor_matches_allow -import itsdangerous import time +import itsdangerous + +from datasette import Permission, hookimpl +from datasette.utils import actor_matches_allow + @hookimpl def register_permissions(): diff --git a/datasette/facets.py b/datasette/facets.py index 7fb0c68b..4420832c 100644 --- a/datasette/facets.py +++ b/datasette/facets.py @@ -1,12 +1,13 @@ import json import urllib + from datasette import hookimpl from datasette.database import QueryInterrupted from datasette.utils import ( + detect_json1, escape_sqlite, path_with_added_args, path_with_removed_args, - detect_json1, sqlite3, ) diff --git a/datasette/filters.py b/datasette/filters.py index 5ea3488b..0132b003 100644 --- a/datasette/filters.py +++ b/datasette/filters.py @@ -1,8 +1,10 @@ -from datasette import hookimpl -from datasette.views.base import DatasetteError -from datasette.utils.asgi import BadRequest import json import numbers + +from datasette import hookimpl +from datasette.utils.asgi import BadRequest +from datasette.views.base import DatasetteError + from .utils import detect_json1, escape_sqlite, path_with_removed_args diff --git a/datasette/forbidden.py b/datasette/forbidden.py index 156a44d4..de1edb5b 100644 --- a/datasette/forbidden.py +++ b/datasette/forbidden.py @@ -1,5 +1,6 @@ from os import stat -from datasette import hookimpl, Response + +from datasette import Response, hookimpl @hookimpl(trylast=True) diff --git a/datasette/handle_exception.py b/datasette/handle_exception.py index 8b7e83e3..13fa23b1 100644 --- a/datasette/handle_exception.py +++ b/datasette/handle_exception.py @@ -1,14 +1,14 @@ -from datasette import hookimpl, Response -from .utils import await_me_maybe, add_cors_headers -from .utils.asgi import ( - Base400, - Forbidden, -) -from .views.base import DatasetteError -from markupsafe import Markup import pdb import traceback + +from markupsafe import Markup + +from datasette import Response, hookimpl + from .plugins import pm +from .utils import add_cors_headers, await_me_maybe +from .utils.asgi import Base400, Forbidden +from .views.base import DatasetteError try: import rich diff --git a/datasette/hookspecs.py b/datasette/hookspecs.py index 801073fc..500d1b33 100644 --- a/datasette/hookspecs.py +++ b/datasette/hookspecs.py @@ -1,5 +1,4 @@ -from pluggy import HookimplMarker -from pluggy import HookspecMarker +from pluggy import HookimplMarker, HookspecMarker hookspec = HookspecMarker("datasette") hookimpl = HookimplMarker("datasette") diff --git a/datasette/inspect.py b/datasette/inspect.py index ede142d0..18bbf860 100644 --- a/datasette/inspect.py +++ b/datasette/inspect.py @@ -1,16 +1,15 @@ import hashlib from .utils import ( - detect_spatialite, detect_fts, detect_primary_keys, + detect_spatialite, escape_sqlite, get_all_foreign_keys, - table_columns, sqlite3, + table_columns, ) - HASH_BLOCK_SIZE = 1024 * 1024 diff --git a/datasette/plugins.py b/datasette/plugins.py index fef0c8e9..bc4aade5 100644 --- a/datasette/plugins.py +++ b/datasette/plugins.py @@ -1,7 +1,9 @@ import importlib -import pluggy -import pkg_resources import sys + +import pkg_resources +import pluggy + from . import hookspecs DEFAULT_PLUGINS = ( diff --git a/datasette/publish/cloudrun.py b/datasette/publish/cloudrun.py index 760ff0d1..caecee93 100644 --- a/datasette/publish/cloudrun.py +++ b/datasette/publish/cloudrun.py @@ -1,15 +1,17 @@ -from datasette import hookimpl -import click import json import os import re from subprocess import check_call, check_output +import click + +from datasette import hookimpl + +from ..utils import temporary_docker_directory from .common import ( add_common_publish_arguments_and_options, fail_if_publish_binary_not_installed, ) -from ..utils import temporary_docker_directory @hookimpl diff --git a/datasette/publish/common.py b/datasette/publish/common.py index 29665eb3..63c66a3f 100644 --- a/datasette/publish/common.py +++ b/datasette/publish/common.py @@ -1,9 +1,11 @@ -from ..utils import StaticMount -import click import os import shutil import sys +import click + +from ..utils import StaticMount + def add_common_publish_arguments_and_options(subcommand): for decorator in reversed( diff --git a/datasette/publish/heroku.py b/datasette/publish/heroku.py index f576a346..cc24be31 100644 --- a/datasette/publish/heroku.py +++ b/datasette/publish/heroku.py @@ -1,19 +1,21 @@ -from contextlib import contextmanager -from datasette import hookimpl -import click import json import os import pathlib import shlex import shutil -from subprocess import call, check_output import tempfile +from contextlib import contextmanager +from subprocess import call, check_output + +import click + +from datasette import hookimpl +from datasette.utils import link_or_copy, link_or_copy_directory, parse_metadata from .common import ( add_common_publish_arguments_and_options, fail_if_publish_binary_not_installed, ) -from datasette.utils import link_or_copy, link_or_copy_directory, parse_metadata @hookimpl diff --git a/datasette/renderer.py b/datasette/renderer.py index 224031a7..f743b46e 100644 --- a/datasette/renderer.py +++ b/datasette/renderer.py @@ -1,10 +1,11 @@ import json + from datasette.utils import ( - value_as_boolean, - remove_infinites, CustomJSONEncoder, path_from_row_pks, + remove_infinites, sqlite3, + value_as_boolean, ) from datasette.utils.asgi import Response diff --git a/datasette/tracer.py b/datasette/tracer.py index fc7338b0..0ad1b556 100644 --- a/datasette/tracer.py +++ b/datasette/tracer.py @@ -1,10 +1,11 @@ import asyncio +import json +import time +import traceback from contextlib import contextmanager from contextvars import ContextVar + from markupsafe import escape -import time -import json -import traceback tracers = {} diff --git a/datasette/url_builder.py b/datasette/url_builder.py index 574bf3c1..dc2108a2 100644 --- a/datasette/url_builder.py +++ b/datasette/url_builder.py @@ -1,6 +1,7 @@ -from .utils import tilde_encode, path_with_format, HASH_LENGTH, PrefixedUrlString import urllib +from .utils import HASH_LENGTH, PrefixedUrlString, path_with_format, tilde_encode + class Urls: def __init__(self, ds): diff --git a/datasette/utils/__init__.py b/datasette/utils/__init__.py index c388673d..2ea4f07d 100644 --- a/datasette/utils/__init__.py +++ b/datasette/utils/__init__.py @@ -1,28 +1,29 @@ import asyncio -from contextlib import contextmanager -import click -from collections import OrderedDict, namedtuple, Counter import base64 import hashlib import inspect import json -import markupsafe -import mergedeep import os import re +import secrets import shlex +import shutil import tempfile -import typing import time import types -import secrets -import shutil +import typing import urllib +from collections import Counter, OrderedDict, namedtuple +from contextlib import contextmanager + +import click +import markupsafe +import mergedeep import yaml + from .shutil_backport import copytree from .sqlite import sqlite3, supports_table_xinfo - # From https://www.sqlite.org/lang_keywords.html reserved_words = set( ( diff --git a/datasette/utils/asgi.py b/datasette/utils/asgi.py index b2c6f3ab..41de474a 100644 --- a/datasette/utils/asgi.py +++ b/datasette/utils/asgi.py @@ -1,12 +1,14 @@ import json -from datasette.utils import MultiParams +from http.cookies import Morsel, SimpleCookie from mimetypes import guess_type -from urllib.parse import parse_qs, urlunparse, parse_qsl from pathlib import Path -from http.cookies import SimpleCookie, Morsel +from urllib.parse import parse_qs, parse_qsl, urlunparse + import aiofiles import aiofiles.os +from datasette.utils import MultiParams + # Workaround for adding samesite support to pre 3.8 python Morsel._reserved["samesite"] = "SameSite" # Thanks, Starlette: diff --git a/datasette/utils/check_callable.py b/datasette/utils/check_callable.py index 5b8a30ac..7e0875ab 100644 --- a/datasette/utils/check_callable.py +++ b/datasette/utils/check_callable.py @@ -1,6 +1,6 @@ import asyncio import types -from typing import NamedTuple, Any +from typing import Any, NamedTuple class CallableStatus(NamedTuple): diff --git a/datasette/utils/internal_db.py b/datasette/utils/internal_db.py index e4b49e80..cd40e385 100644 --- a/datasette/utils/internal_db.py +++ b/datasette/utils/internal_db.py @@ -1,4 +1,5 @@ import textwrap + from datasette.utils import table_column_details diff --git a/datasette/utils/shutil_backport.py b/datasette/utils/shutil_backport.py index dbe22404..7fe82709 100644 --- a/datasette/utils/shutil_backport.py +++ b/datasette/utils/shutil_backport.py @@ -5,7 +5,7 @@ This code is licensed under the Python License: https://github.com/python/cpython/blob/v3.8.3/LICENSE """ import os -from shutil import copy, copy2, copystat, Error +from shutil import Error, copy, copy2, copystat def _copytree( diff --git a/datasette/utils/testing.py b/datasette/utils/testing.py index d4990784..50b1f60d 100644 --- a/datasette/utils/testing.py +++ b/datasette/utils/testing.py @@ -1,6 +1,7 @@ -from asgiref.sync import async_to_sync -from urllib.parse import urlencode import json +from urllib.parse import urlencode + +from asgiref.sync import async_to_sync # These wrapper classes pre-date the introduction of # datasette.client and httpx to Datasette. They could diff --git a/datasette/views/base.py b/datasette/views/base.py index 0080b33c..c23c7417 100644 --- a/datasette/views/base.py +++ b/datasette/views/base.py @@ -5,33 +5,26 @@ import sys import textwrap import time import urllib -from markupsafe import escape - import pint +from markupsafe import escape from datasette import __version__ from datasette.database import QueryInterrupted -from datasette.utils.asgi import Request from datasette.utils import ( - add_cors_headers, - await_me_maybe, EscapeHtmlWriter, InvalidSql, LimitedWriter, + add_cors_headers, + await_me_maybe, call_with_supported_arguments, path_from_row_pks, path_with_added_args, - path_with_removed_args, path_with_format, + path_with_removed_args, sqlite3, ) -from datasette.utils.asgi import ( - AsgiStream, - NotFound, - Response, - BadRequest, -) +from datasette.utils.asgi import AsgiStream, BadRequest, NotFound, Request, Response ureg = pint.UnitRegistry() diff --git a/datasette/views/database.py b/datasette/views/database.py index d9abc38a..8c7d5643 100644 --- a/datasette/views/database.py +++ b/datasette/views/database.py @@ -1,36 +1,37 @@ -from dataclasses import dataclass, field -from typing import Callable -from urllib.parse import parse_qsl, urlencode import asyncio import hashlib import itertools import json -import markupsafe import os import re -import sqlite_utils import textwrap +from dataclasses import dataclass, field +from typing import Callable +from urllib.parse import parse_qsl, urlencode + +import markupsafe +import sqlite_utils from datasette.database import QueryInterrupted +from datasette.plugins import pm from datasette.utils import ( + InvalidSql, add_cors_headers, await_me_maybe, call_with_supported_arguments, derive_named_parameters, format_bytes, - tilde_decode, - to_css_class, - validate_sql_select, is_url, path_with_added_args, path_with_format, path_with_removed_args, sqlite3, + tilde_decode, + to_css_class, truncate_url, - InvalidSql, + validate_sql_select, ) -from datasette.utils.asgi import AsgiFileDownload, NotFound, Response, Forbidden -from datasette.plugins import pm +from datasette.utils.asgi import AsgiFileDownload, Forbidden, NotFound, Response from .base import BaseView, DatasetteError, View, _error, stream_csv diff --git a/datasette/views/index.py b/datasette/views/index.py index 95b29302..1fb44d68 100644 --- a/datasette/views/index.py +++ b/datasette/views/index.py @@ -1,13 +1,12 @@ import hashlib import json -from datasette.utils import add_cors_headers, CustomJSONEncoder +from datasette.utils import CustomJSONEncoder, add_cors_headers from datasette.utils.asgi import Response from datasette.version import __version__ from .base import BaseView - # Truncate table list on homepage at: TRUNCATE_AT = 5 diff --git a/datasette/views/row.py b/datasette/views/row.py index 8f07a662..2d437fab 100644 --- a/datasette/views/row.py +++ b/datasette/views/row.py @@ -1,15 +1,18 @@ -from datasette.utils.asgi import NotFound, Forbidden, Response +import json + +import sqlite_utils + from datasette.database import QueryInterrupted -from .base import DataView, BaseView, _error from datasette.utils import ( - tilde_decode, - urlsafe_components, - to_css_class, escape_sqlite, row_sql_params_pks, + tilde_decode, + to_css_class, + urlsafe_components, ) -import json -import sqlite_utils +from datasette.utils.asgi import Forbidden, NotFound, Response + +from .base import BaseView, DataView, _error from .table import display_columns_and_rows @@ -152,7 +155,7 @@ class RowError(Exception): async def _resolve_row_and_check_permission(datasette, request, permission): - from datasette.app import DatabaseNotFound, TableNotFound, RowNotFound + from datasette.app import DatabaseNotFound, RowNotFound, TableNotFound try: resolved = await datasette.resolve_row(request) diff --git a/datasette/views/special.py b/datasette/views/special.py index c45a3eca..74e9b2cd 100644 --- a/datasette/views/special.py +++ b/datasette/views/special.py @@ -1,14 +1,16 @@ import json -from datasette.utils.asgi import Response, Forbidden +import secrets +import urllib + from datasette.utils import ( actor_matches_allow, add_cors_headers, - tilde_encode, tilde_decode, + tilde_encode, ) +from datasette.utils.asgi import Forbidden, Response + from .base import BaseView, View -import secrets -import urllib class JsonDataView(BaseView): diff --git a/datasette/views/table.py b/datasette/views/table.py index 6df8b915..0bdf1298 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -3,40 +3,41 @@ import itertools import json import urllib -from asyncinject import Registry import markupsafe +import sqlite_utils +from asyncinject import Registry -from datasette.plugins import pm -from datasette.database import QueryInterrupted from datasette import tracer +from datasette.database import QueryInterrupted +from datasette.filters import Filters +from datasette.plugins import pm from datasette.utils import ( + CustomRow, + InvalidSql, add_cors_headers, + append_querystring, await_me_maybe, call_with_supported_arguments, - CustomRow, - append_querystring, compound_keys_after_sql, - format_bytes, - tilde_encode, escape_sqlite, filters_should_redirect, + format_bytes, is_url, path_from_row_pks, path_with_added_args, path_with_format, path_with_removed_args, path_with_replaced_args, + sqlite3, + tilde_encode, to_css_class, truncate_url, urlsafe_components, value_as_boolean, - InvalidSql, - sqlite3, ) from datasette.utils.asgi import BadRequest, Forbidden, NotFound, Response -from datasette.filters import Filters -import sqlite_utils -from .base import BaseView, DatasetteError, ureg, _error, stream_csv + +from .base import BaseView, DatasetteError, _error, stream_csv, ureg from .database import QueryView LINK_WITH_LABEL = ( diff --git a/docs/metadata_doc.py b/docs/metadata_doc.py index 537830ca..a51de509 100644 --- a/docs/metadata_doc.py +++ b/docs/metadata_doc.py @@ -1,7 +1,8 @@ import json import textwrap -from yaml import safe_dump + from ruamel.yaml import round_trip_load +from yaml import safe_dump def metadata_example(cog, data=None, yaml=None): diff --git a/setup.py b/setup.py index 3a105523..332d91ec 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup, find_packages import os +from setuptools import find_packages, setup + def get_long_description(): with open( diff --git a/tests/conftest.py b/tests/conftest.py index fb7f768e..1fdce671 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,15 @@ import asyncio -import httpx import os import pathlib -import pytest -import pytest_asyncio import re import subprocess import tempfile import time -import trustme +import httpx +import pytest +import pytest_asyncio +import trustme try: import pysqlite3 as sqlite3 @@ -41,6 +41,7 @@ def wait_until_responds(url, timeout=5.0, client=httpx, **kwargs): @pytest_asyncio.fixture async def ds_client(): from datasette.app import Datasette + from .fixtures import METADATA, PLUGINS_DIR global _ds_client @@ -59,7 +60,7 @@ async def ds_client(): "num_sql_threads": 1, }, ) - from .fixtures import TABLES, TABLE_PARAMETERIZED_SQL + from .fixtures import TABLE_PARAMETERIZED_SQL, TABLES db = ds.add_memory_database("fixtures") ds.remove_database("_memory") diff --git a/tests/fixtures.py b/tests/fixtures.py index a6700239..8b5ffe4e 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,18 +1,19 @@ -from datasette.app import Datasette -from datasette.utils.sqlite import sqlite3 -from datasette.utils.testing import TestClient -import click import contextlib import itertools import json import os import pathlib -import pytest import random import string import tempfile import textwrap +import click +import pytest + +from datasette.app import Datasette +from datasette.utils.sqlite import sqlite3 +from datasette.utils.testing import TestClient # This temp file is used by one of the plugin config tests TEMP_PLUGIN_SECRET_FILE = os.path.join(tempfile.gettempdir(), "plugin-secret") diff --git a/tests/plugins/my_plugin.py b/tests/plugins/my_plugin.py index eb70d9bd..ce57f9f8 100644 --- a/tests/plugins/my_plugin.py +++ b/tests/plugins/my_plugin.py @@ -1,13 +1,14 @@ import asyncio -from datasette import hookimpl, Permission -from datasette.facets import Facet -from datasette import tracer -from datasette.utils import path_with_added_args -from datasette.utils.asgi import asgi_send_json, Response import base64 -import pint import json +import pint + +from datasette import Permission, hookimpl, tracer +from datasette.facets import Facet +from datasette.utils import path_with_added_args +from datasette.utils.asgi import Response, asgi_send_json + ureg = pint.UnitRegistry() @@ -326,11 +327,7 @@ def startup(datasette): datasette._startup_hook_fired = True # And test some import shortcuts too - from datasette import Response - from datasette import Forbidden - from datasette import NotFound - from datasette import hookimpl - from datasette import actor_matches_allow + from datasette import Forbidden, NotFound, Response, actor_matches_allow, hookimpl _ = (Response, Forbidden, NotFound, hookimpl, actor_matches_allow) diff --git a/tests/plugins/my_plugin_2.py b/tests/plugins/my_plugin_2.py index d588342c..31467b8e 100644 --- a/tests/plugins/my_plugin_2.py +++ b/tests/plugins/my_plugin_2.py @@ -1,8 +1,10 @@ +import json +from functools import wraps + +import markupsafe + from datasette import hookimpl from datasette.utils.asgi import Response -from functools import wraps -import markupsafe -import json @hookimpl diff --git a/tests/plugins/register_output_renderer.py b/tests/plugins/register_output_renderer.py index cfe15215..1bb66003 100644 --- a/tests/plugins/register_output_renderer.py +++ b/tests/plugins/register_output_renderer.py @@ -1,6 +1,7 @@ +import json + from datasette import hookimpl from datasette.utils.asgi import Response -import json async def can_render( diff --git a/tests/plugins/sleep_sql_function.py b/tests/plugins/sleep_sql_function.py index d4b32a09..2fca1d66 100644 --- a/tests/plugins/sleep_sql_function.py +++ b/tests/plugins/sleep_sql_function.py @@ -1,6 +1,7 @@ -from datasette import hookimpl import time +from datasette import hookimpl + @hookimpl def prepare_connection(conn): diff --git a/tests/test_api.py b/tests/test_api.py index f96f571e..cfd937c3 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,26 +1,29 @@ +import pathlib +import sys +import urllib + +import pytest + from datasette.app import Datasette from datasette.plugins import DEFAULT_PLUGINS from datasette.utils.sqlite import supports_table_xinfo from datasette.version import __version__ + from .fixtures import ( # noqa - app_client, - app_client_no_files, - app_client_with_dot, - app_client_shorter_time_limit, - app_client_two_attached_databases_one_immutable, - app_client_larger_cache_size, - app_client_with_cors, - app_client_two_attached_databases, - app_client_conflicting_database_names, - app_client_immutable_and_inspect_file, - make_app_client, EXPECTED_PLUGINS, METADATA, + app_client, + app_client_conflicting_database_names, + app_client_immutable_and_inspect_file, + app_client_larger_cache_size, + app_client_no_files, + app_client_shorter_time_limit, + app_client_two_attached_databases, + app_client_two_attached_databases_one_immutable, + app_client_with_cors, + app_client_with_dot, + make_app_client, ) -import pathlib -import pytest -import sys -import urllib @pytest.mark.asyncio diff --git a/tests/test_api_write.py b/tests/test_api_write.py index f27d143f..4202c80b 100644 --- a/tests/test_api_write.py +++ b/tests/test_api_write.py @@ -1,7 +1,9 @@ +import time + +import pytest + from datasette.app import Datasette from datasette.utils import sqlite3 -import pytest -import time @pytest.fixture diff --git a/tests/test_auth.py b/tests/test_auth.py index 33cf9b35..4a0d0475 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,11 +1,14 @@ +import time + +import pytest from bs4 import BeautifulSoup as Soup +from click.testing import CliRunner + +from datasette.cli import cli +from datasette.utils import baseconv + from .fixtures import app_client from .utils import cookie_was_deleted -from click.testing import CliRunner -from datasette.utils import baseconv -from datasette.cli import cli -import pytest -import time @pytest.mark.asyncio diff --git a/tests/test_base_view.py b/tests/test_base_view.py index 2cd4d601..0ce17123 100644 --- a/tests/test_base_view.py +++ b/tests/test_base_view.py @@ -1,8 +1,10 @@ -from datasette.views.base import View +import json + +import pytest + from datasette import Request, Response from datasette.app import Datasette -import json -import pytest +from datasette.views.base import View class GetView(View): diff --git a/tests/test_black.py b/tests/test_black.py index d09b2514..6a534724 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1,8 +1,9 @@ -import black -from click.testing import CliRunner -from pathlib import Path -import pytest import sys +from pathlib import Path + +import black +import pytest +from click.testing import CliRunner code_root = Path(__file__).parent.parent diff --git a/tests/test_canned_queries.py b/tests/test_canned_queries.py index 5256c24c..89aa2333 100644 --- a/tests/test_canned_queries.py +++ b/tests/test_canned_queries.py @@ -1,8 +1,10 @@ -from bs4 import BeautifulSoup as Soup import json -import pytest import re -from .fixtures import make_app_client, app_client + +import pytest +from bs4 import BeautifulSoup as Soup + +from .fixtures import app_client, make_app_client @pytest.fixture diff --git a/tests/test_cli.py b/tests/test_cli.py index 71f0bbe3..82220e98 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,25 +1,25 @@ -from .fixtures import ( - app_client, - make_app_client, - TestClient as _TestClient, - EXPECTED_PLUGINS, -) import asyncio -from datasette.app import SETTINGS -from datasette.plugins import DEFAULT_PLUGINS -from datasette.cli import cli, serve -from datasette.version import __version__ -from datasette.utils import tilde_encode -from datasette.utils.sqlite import sqlite3 -from click.testing import CliRunner import io import json import pathlib -import pytest import sys import textwrap -from unittest import mock import urllib +from unittest import mock + +import pytest +from click.testing import CliRunner + +from datasette.app import SETTINGS +from datasette.cli import cli, serve +from datasette.plugins import DEFAULT_PLUGINS +from datasette.utils import tilde_encode +from datasette.utils.sqlite import sqlite3 +from datasette.version import __version__ + +from .fixtures import EXPECTED_PLUGINS +from .fixtures import TestClient as _TestClient +from .fixtures import app_client, make_app_client def test_inspect_cli(app_client): diff --git a/tests/test_cli_serve_get.py b/tests/test_cli_serve_get.py index dc7fc1e2..1a9316f6 100644 --- a/tests/test_cli_serve_get.py +++ b/tests/test_cli_serve_get.py @@ -1,8 +1,10 @@ +import json +import textwrap + +from click.testing import CliRunner + from datasette.cli import cli, serve from datasette.plugins import pm -from click.testing import CliRunner -import textwrap -import json def test_serve_with_get(tmp_path_factory): diff --git a/tests/test_cli_serve_server.py b/tests/test_cli_serve_server.py index 47f23c08..b7604bb8 100644 --- a/tests/test_cli_serve_server.py +++ b/tests/test_cli_serve_server.py @@ -1,6 +1,7 @@ +import socket + import httpx import pytest -import socket @pytest.mark.serial diff --git a/tests/test_config_dir.py b/tests/test_config_dir.py index 748412c3..eb5a4cf9 100644 --- a/tests/test_config_dir.py +++ b/tests/test_config_dir.py @@ -1,13 +1,15 @@ import json import pathlib + import pytest +from click.testing import CliRunner from datasette.app import Datasette from datasette.cli import cli -from datasette.utils.sqlite import sqlite3 from datasette.utils import StartupError +from datasette.utils.sqlite import sqlite3 + from .fixtures import TestClient as _TestClient -from click.testing import CliRunner PLUGIN = """ from datasette import hookimpl diff --git a/tests/test_crossdb.py b/tests/test_crossdb.py index 01c51130..4b7a10c3 100644 --- a/tests/test_crossdb.py +++ b/tests/test_crossdb.py @@ -1,7 +1,10 @@ -from datasette.cli import cli -from click.testing import CliRunner -import urllib import sqlite3 +import urllib + +from click.testing import CliRunner + +from datasette.cli import cli + from .fixtures import app_client_two_attached_databases_crossdb_enabled diff --git a/tests/test_csv.py b/tests/test_csv.py index ed83d685..e2504cc8 100644 --- a/tests/test_csv.py +++ b/tests/test_csv.py @@ -1,12 +1,14 @@ -from bs4 import BeautifulSoup as Soup +import urllib.parse + import pytest +from bs4 import BeautifulSoup as Soup + from .fixtures import ( # noqa app_client, app_client_csv_max_mb_one, app_client_with_cors, app_client_with_trace, ) -import urllib.parse EXPECTED_TABLE_CSV = """id,content 1,hello diff --git a/tests/test_custom_pages.py b/tests/test_custom_pages.py index f2cfe394..a1ffe3b1 100644 --- a/tests/test_custom_pages.py +++ b/tests/test_custom_pages.py @@ -1,5 +1,7 @@ import pathlib + import pytest + from .fixtures import make_app_client TEST_TEMPLATE_DIRS = str(pathlib.Path(__file__).parent / "test_templates") diff --git a/tests/test_docs.py b/tests/test_docs.py index e9b813fe..911597e5 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,13 +1,15 @@ """ Tests to ensure certain things are documented. """ +import re +from pathlib import Path + +import pytest from click.testing import CliRunner + from datasette import app, utils from datasette.cli import cli from datasette.filters import Filters -from pathlib import Path -import pytest -import re docs_path = Path(__file__).parent.parent / "docs" label_re = re.compile(r"\.\. _([^\s:]+):") diff --git a/tests/test_facets.py b/tests/test_facets.py index 48cc0ff2..04f11fb4 100644 --- a/tests/test_facets.py +++ b/tests/test_facets.py @@ -1,11 +1,14 @@ +import json + +import pytest + from datasette.app import Datasette from datasette.database import Database -from datasette.facets import ColumnFacet, ArrayFacet, DateFacet -from datasette.utils.asgi import Request +from datasette.facets import ArrayFacet, ColumnFacet, DateFacet from datasette.utils import detect_json1 +from datasette.utils.asgi import Request + from .fixtures import make_app_client -import json -import pytest @pytest.mark.asyncio diff --git a/tests/test_filters.py b/tests/test_filters.py index 5b2e9636..586a7a68 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,7 +1,8 @@ -from datasette.filters import Filters, through_filters, where_filters, search_filters -from datasette.utils.asgi import Request import pytest +from datasette.filters import Filters, search_filters, through_filters, where_filters +from datasette.utils.asgi import Request + @pytest.mark.parametrize( "args,expected_where,expected_params", diff --git a/tests/test_html.py b/tests/test_html.py index ffc2aef1..09bb6963 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -1,19 +1,22 @@ +import json +import pathlib +import re +import urllib.parse + +import pytest from bs4 import BeautifulSoup as Soup + from datasette.utils import allowed_pragmas + from .fixtures import ( # noqa + METADATA, app_client, app_client_base_url_prefix, app_client_shorter_time_limit, app_client_two_attached_databases, make_app_client, - METADATA, ) from .utils import assert_footer_links, inner_html -import json -import pathlib -import pytest -import re -import urllib.parse def test_homepage(app_client_two_attached_databases): diff --git a/tests/test_internals_database.py b/tests/test_internals_database.py index 647ae7bd..eb270251 100644 --- a/tests/test_internals_database.py +++ b/tests/test_internals_database.py @@ -1,14 +1,17 @@ """ Tests for the datasette.database.Database class """ -from datasette.database import Database, Results, MultipleValues -from datasette.utils.sqlite import sqlite3 -from datasette.utils import Column -from .fixtures import app_client, app_client_two_attached_databases_crossdb_enabled -import pytest import time import uuid +import pytest + +from datasette.database import Database, MultipleValues, Results +from datasette.utils import Column +from datasette.utils.sqlite import sqlite3 + +from .fixtures import app_client, app_client_two_attached_databases_crossdb_enabled + @pytest.fixture def db(app_client): diff --git a/tests/test_internals_datasette.py b/tests/test_internals_datasette.py index d59ff729..460df557 100644 --- a/tests/test_internals_datasette.py +++ b/tests/test_internals_datasette.py @@ -2,12 +2,14 @@ Tests for the datasette.app.Datasette class """ import dataclasses -from datasette import Forbidden, Context -from datasette.app import Datasette, Database -from itsdangerous import BadSignature -import pytest from typing import Optional +import pytest +from itsdangerous import BadSignature + +from datasette import Context, Forbidden +from datasette.app import Database, Datasette + @pytest.fixture def datasette(ds_client): diff --git a/tests/test_internals_request.py b/tests/test_internals_request.py index d1ca1f46..ce29bbd1 100644 --- a/tests/test_internals_request.py +++ b/tests/test_internals_request.py @@ -1,7 +1,9 @@ -from datasette.utils.asgi import Request import json + import pytest +from datasette.utils.asgi import Request + @pytest.mark.asyncio async def test_request_post_vars(): diff --git a/tests/test_internals_response.py b/tests/test_internals_response.py index 820b20b2..358aeb38 100644 --- a/tests/test_internals_response.py +++ b/tests/test_internals_response.py @@ -1,6 +1,7 @@ -from datasette.utils.asgi import Response import pytest +from datasette.utils.asgi import Response + def test_response_html(): response = Response.html("Hello from HTML") diff --git a/tests/test_internals_urls.py b/tests/test_internals_urls.py index d60aafcf..549230e8 100644 --- a/tests/test_internals_urls.py +++ b/tests/test_internals_urls.py @@ -1,6 +1,7 @@ +import pytest + from datasette.app import Datasette from datasette.utils import PrefixedUrlString -import pytest @pytest.fixture(scope="module") diff --git a/tests/test_load_extensions.py b/tests/test_load_extensions.py index 4007e0be..f9ff644b 100644 --- a/tests/test_load_extensions.py +++ b/tests/test_load_extensions.py @@ -1,7 +1,9 @@ -from datasette.app import Datasette -import pytest from pathlib import Path +import pytest + +from datasette.app import Datasette + # not necessarily a full path - the full compiled path looks like "ext.dylib" # or another suffix, but sqlite will, under the hood, decide which file # extension to use based on the operating system (apple=dylib, windows=dll etc) diff --git a/tests/test_messages.py b/tests/test_messages.py index a7e4d046..9078b1cc 100644 --- a/tests/test_messages.py +++ b/tests/test_messages.py @@ -1,6 +1,7 @@ -from .utils import cookie_was_deleted import pytest +from .utils import cookie_was_deleted + @pytest.mark.asyncio @pytest.mark.parametrize( diff --git a/tests/test_package.py b/tests/test_package.py index f05f3ece..43b20589 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -1,9 +1,11 @@ -from click.testing import CliRunner -from datasette import cli -from unittest import mock import os import pathlib +from unittest import mock + import pytest +from click.testing import CliRunner + +from datasette import cli class CaptureDockerfile: diff --git a/tests/test_permissions.py b/tests/test_permissions.py index f940d486..21ba29cc 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -1,17 +1,20 @@ import collections -from datasette.app import Datasette -from datasette.cli import cli -from .fixtures import app_client, assert_permissions_checked, make_app_client -from click.testing import CliRunner -from bs4 import BeautifulSoup as Soup import copy import json -from pprint import pprint -import pytest_asyncio -import pytest import re import time import urllib +from pprint import pprint + +import pytest +import pytest_asyncio +from bs4 import BeautifulSoup as Soup +from click.testing import CliRunner + +from datasette.app import Datasette +from datasette.cli import cli + +from .fixtures import app_client, assert_permissions_checked, make_app_client @pytest.fixture(scope="module") diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 28fe720f..348c4c13 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,21 +1,3 @@ -from bs4 import BeautifulSoup as Soup -from .fixtures import ( - app_client, - app_client, - make_app_client, - TABLES, - TEMP_PLUGIN_SECRET_FILE, - PLUGINS_DIR, - TestClient as _TestClient, -) # noqa -from click.testing import CliRunner -from datasette.app import Datasette -from datasette import cli, hookimpl, Permission -from datasette.filters import FilterArguments -from datasette.plugins import get_plugins, DEFAULT_PLUGINS, pm -from datasette.utils.sqlite import sqlite3 -from datasette.utils import CustomRow, StartupError -from jinja2.environment import Template import base64 import importlib import json @@ -23,9 +5,24 @@ import os import pathlib import re import textwrap -import pytest import urllib +import pytest +from bs4 import BeautifulSoup as Soup +from click.testing import CliRunner +from jinja2.environment import Template + +from datasette import Permission, cli, hookimpl +from datasette.app import Datasette +from datasette.filters import FilterArguments +from datasette.plugins import DEFAULT_PLUGINS, get_plugins, pm +from datasette.utils import CustomRow, StartupError +from datasette.utils.sqlite import sqlite3 + +from .fixtures import PLUGINS_DIR, TABLES, TEMP_PLUGIN_SECRET_FILE +from .fixtures import TestClient as _TestClient # noqa +from .fixtures import app_client, make_app_client + at_memory_re = re.compile(r" at 0x\w+") diff --git a/tests/test_publish_cloudrun.py b/tests/test_publish_cloudrun.py index 818fa2d3..12796f1f 100644 --- a/tests/test_publish_cloudrun.py +++ b/tests/test_publish_cloudrun.py @@ -1,10 +1,12 @@ -from click.testing import CliRunner -from datasette import cli -from unittest import mock import json import os -import pytest import textwrap +from unittest import mock + +import pytest +from click.testing import CliRunner + +from datasette import cli @pytest.mark.serial diff --git a/tests/test_publish_heroku.py b/tests/test_publish_heroku.py index cab83654..4302ed94 100644 --- a/tests/test_publish_heroku.py +++ b/tests/test_publish_heroku.py @@ -1,9 +1,11 @@ -from click.testing import CliRunner -from datasette import cli -from unittest import mock import os import pathlib +from unittest import mock + import pytest +from click.testing import CliRunner + +from datasette import cli @pytest.mark.serial diff --git a/tests/test_routes.py b/tests/test_routes.py index 85945dec..8238a171 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,8 +1,9 @@ -from datasette.app import Datasette, Database -from datasette.utils import resolve_routes import pytest import pytest_asyncio +from datasette.app import Database, Datasette +from datasette.utils import resolve_routes + @pytest.fixture(scope="session") def routes(): diff --git a/tests/test_spatialite.py b/tests/test_spatialite.py index c07a30e8..0d11737e 100644 --- a/tests/test_spatialite.py +++ b/tests/test_spatialite.py @@ -1,8 +1,10 @@ -from datasette.app import Datasette -from datasette.utils import find_spatialite, SpatialiteNotFound, SPATIALITE_FUNCTIONS -from .utils import has_load_extension import pytest +from datasette.app import Datasette +from datasette.utils import SPATIALITE_FUNCTIONS, SpatialiteNotFound, find_spatialite + +from .utils import has_load_extension + def has_spatialite(): try: diff --git a/tests/test_table_api.py b/tests/test_table_api.py index 46d1c9b8..245b2d3f 100644 --- a/tests/test_table_api.py +++ b/tests/test_table_api.py @@ -1,16 +1,19 @@ +import json +import urllib + +import pytest + from datasette.utils import detect_json1 from datasette.utils.sqlite import sqlite_version + from .fixtures import ( # noqa app_client, - app_client_with_trace, app_client_returned_rows_matches_page_size, + app_client_with_trace, generate_compound_rows, generate_sortable_rows, make_app_client, ) -import json -import pytest -import urllib @pytest.mark.asyncio diff --git a/tests/test_table_html.py b/tests/test_table_html.py index c4c7878c..2342a007 100644 --- a/tests/test_table_html.py +++ b/tests/test_table_html.py @@ -1,12 +1,12 @@ -from datasette.app import Datasette, Database -from bs4 import BeautifulSoup as Soup -from .fixtures import ( # noqa - app_client, - make_app_client, -) import pathlib -import pytest import urllib.parse + +import pytest +from bs4 import BeautifulSoup as Soup + +from datasette.app import Database, Datasette + +from .fixtures import app_client, make_app_client # noqa from .utils import assert_footer_links, inner_html diff --git a/tests/test_tracer.py b/tests/test_tracer.py index ceadee50..9cda418f 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -1,4 +1,5 @@ import pytest + from .fixtures import make_app_client diff --git a/tests/test_utils.py b/tests/test_utils.py index 8b64f865..46ad7d6d 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,17 +1,19 @@ """ Tests for various datasette helper functions. """ -from datasette.app import Datasette -from datasette import utils -from datasette.utils.asgi import Request -from datasette.utils.sqlite import sqlite3 import json import os import pathlib -import pytest import tempfile from unittest.mock import patch +import pytest + +from datasette import utils +from datasette.app import Datasette +from datasette.utils.asgi import Request +from datasette.utils.sqlite import sqlite3 + @pytest.mark.parametrize( "path,expected", diff --git a/tests/test_utils_check_callable.py b/tests/test_utils_check_callable.py index 4f72f9ff..857b73cd 100644 --- a/tests/test_utils_check_callable.py +++ b/tests/test_utils_check_callable.py @@ -1,6 +1,7 @@ -from datasette.utils.check_callable import check_callable import pytest +from datasette.utils.check_callable import check_callable + class AsyncClass: async def __call__(self):