diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 599bbf2..ee0665c 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -2,7 +2,7 @@ name: acceptance on: pull_request: - types: [ opened, synchronize, ready_for_review ] + types: [ opened, synchronize ] merge_group: types: [ checks_requested ] push: @@ -18,26 +18,30 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + HATCH_VERBOSE: "2" + HATCH_VERSION: "1.16.5" + jobs: integration: - if: github.event_name == 'pull_request' && github.event.pull_request.draft == false + if: github.event_name == 'pull_request' environment: account-admin runs-on: larger steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: cache: 'pip' cache-dependency-path: '**/pyproject.toml' - python-version: '3.10' + python-version: '3.14' - name: Install hatch - run: pip install hatch==1.15.0 + run: pip install "hatch==${HATCH_VERSION}" - name: Fetch relevant branches run: | diff --git a/.github/workflows/downstreams.yml b/.github/workflows/downstreams.yml index 97f3ef2..ab88940 100644 --- a/.github/workflows/downstreams.yml +++ b/.github/workflows/downstreams.yml @@ -20,6 +20,10 @@ permissions: contents: read pull-requests: write +env: + HATCH_VERBOSE: "2" + HATCH_VERSION: "1.16.5" + jobs: compatibility: strategy: @@ -31,20 +35,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4.1.7 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: cache: 'pip' cache-dependency-path: '**/pyproject.toml' + # Downstream currently just tests with system python rather than controlling the versions properly. python-version: '3.10' - name: Install toolchain run: | - pip install hatch==1.15.0 + pip install "hatch==${HATCH_VERSION}" + - name: Downstreams uses: databrickslabs/sandbox/downstreams@acceptance/v0.4.2 with: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 0498aeb..ec94f87 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -14,25 +14,29 @@ permissions: concurrency: group: single-acceptance-job-per-repo +env: + HATCH_VERBOSE: "2" + HATCH_VERSION: "1.16.5" + jobs: integration: environment: account-admin runs-on: larger steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: cache: 'pip' cache-dependency-path: '**/pyproject.toml' - python-version: '3.10' + python-version: '3.14' - name: Install hatch - run: pip install hatch==1.15.0 + run: pip install "hatch==${HATCH_VERSION}" - name: Run nightly tests uses: databrickslabs/sandbox/acceptance@acceptance/v0.4.2 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 408cf04..c4b6704 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -15,7 +15,8 @@ on: - main env: - HATCH_VERSION: 1.15.0 + HATCH_VERBOSE: "2" + HATCH_VERSION: "1.16.5" jobs: ci: @@ -23,25 +24,30 @@ jobs: strategy: fail-fast: false matrix: - pyVersion: [ '3.10', '3.11', '3.12' ] + python: [ '3.10', '3.11', '3.12', '3.13', '3.14' ] + env: + HATCH_ENV: "test.py${{ matrix.python }}" steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: cache: 'pip' cache-dependency-path: '**/pyproject.toml' - python-version: ${{ matrix.pyVersion }} + # The version under test, as well as Python 3.14 for Hatch. + python-version: | + ${{ matrix.python }} + ${{ matrix.python != '3.14' && '3.14' || '' }} - name: Install hatch - run: pip install hatch==$HATCH_VERSION + run: pip install "hatch==${HATCH_VERSION}" - name: Run unit tests - run: hatch run test + run: make test - name: Publish test coverage uses: codecov/codecov-action@v5 @@ -52,19 +58,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: cache: 'pip' cache-dependency-path: '**/pyproject.toml' - python-version: 3.10.x + python-version: '3.14' - name: Install hatch - run: pip install hatch==$HATCH_VERSION + run: pip install "hatch==${HATCH_VERSION}" - name: Reformat code run: make fmt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d779a0..0f46af6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,10 @@ on: tags: - 'v*' +env: + HATCH_VERBOSE: "2" + HATCH_VERSION: "1.16.5" + jobs: publish: runs-on: @@ -17,17 +21,17 @@ jobs: # Used to attach signing artifacts to the published release. contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: cache: 'pip' cache-dependency-path: '**/pyproject.toml' - python-version: '3.10' + python-version: '3.14' - name: Build wheels run: | - pip install hatch==1.15.0 + pip install "hatch==${HATCH_VERSION}" hatch build - name: Draft release diff --git a/.gitignore b/.gitignore index 822e00a..b3203b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea/ .venv/ +.venv-*/ *.iml *.pyc .pytest_cache diff --git a/Makefile b/Makefile index a953697..1550724 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ all: clean lint fmt test coverage clean: - rm -fr .venv clean htmlcov .mypy_cache .pytest_cache .ruff_cache .coverage coverage.xml + rm -fr .venv* clean htmlcov .mypy_cache .pytest_cache .ruff_cache .coverage coverage.xml rm -fr **/*.pyc .venv/bin/python: diff --git a/pyproject.toml b/pyproject.toml index 3d425a7..1467c5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dynamic = ["version"] description = 'Python Testing for Databricks' readme = "README.md" license-files = { paths = ["LICENSE", "NOTICE"] } -requires-python = ">=3.10" +requires-python = ">=3.10,<3.15" keywords = ["Databricks", "pytest"] maintainers = [ { name = "Serge Smertin", email = "serge.smertin@databricks.com" }, @@ -36,6 +36,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Environment :: Console", "Framework :: Pytest", @@ -50,7 +52,7 @@ classifiers = [ dependencies = [ "databricks-sdk>=0.51.0,<1.0", "databricks-labs-lsql>=0.10", - "pytest>=8.3", + "pytest>=9.0", ] [project.entry-points.pytest11] @@ -67,40 +69,70 @@ path = "src/databricks/labs/pytester/__about__.py" [tool.hatch.envs.default] dependencies = [ "black~=24.3.0", - "coverage[toml]~=7.4.4", - "mypy~=1.11.0", - "pylint~=3.2.2", - "pylint-pytest==2.0.0a0", + "coverage[toml]~=7.13.4", + "mypy~=1.19.1", + "pylint~=4.0.5", + "pylint-pytest==2.0.0a1", "databricks-labs-pylint~=0.4.0", - "pytest-cov~=4.1.0", - "pytest-mock~=3.14.0", - "pytest-timeout~=2.3.1", - "pytest-xdist~=3.5.0", + "pytest-cov~=7.0.0", + "pytest-mock~=3.15.1", + "pytest-timeout~=2.4.0", + "pytest-xdist~=3.8.0", "ruff~=0.3.4", "databricks-connect~=15.4.3", ] -# store virtual env as the child of this folder. Helps VSCode (and PyCharm) to run better +python="3.10" + +# Fixed path expected by the `labs` tool to run acceptance tests. path = ".venv" +[tool.hatch.envs.default.env-vars] +MYPY_CACHE_DIR = ".venv/mypy-cache" +PYTEST_ADDOPTS = "-o cache_dir=.venv/pytest-cache" +RUFF_CACHE_DIR = ".venv/ruff-cache" + [tool.hatch.envs.default.scripts] test = "pytest -n 4 --cov src --cov-report=xml --timeout 30 tests/unit --durations 20" coverage = "pytest -n auto --cov src tests/unit --timeout 30 --cov-report=html --durations 20" integration = "pytest -n 10 --cov src tests/integration --durations 20" fmt = ["black . --extend-exclude 'tests/unit/source_code/samples/'", "ruff check . --fix", - "mypy --disable-error-code 'annotation-unchecked' --disable-error-code import-untyped --enable-incomplete-feature=NewGenericSyntax --exclude 'tests/resources/*' --exclude dist .", + "mypy --disable-error-code 'annotation-unchecked' --disable-error-code import-untyped --exclude 'tests/resources/*' --exclude dist .", "pylint --output-format=colorized -j 0 src tests"] verify = ["black --check . --extend-exclude 'tests/unit/source_code/samples/'", "ruff check .", - "mypy --exclude 'tests/resources/*' --exclude dist . --enable-incomplete-feature=NewGenericSyntax", + "mypy --exclude 'tests/resources/*' --exclude dist .", "pylint --output-format=colorized -j 0 src tests"] lint = ["pylint --output-format=colorized -j 0 src tests"] +[tool.hatch.envs.test.env-vars] +MYPY_CACHE_DIR = ".venv-py{matrix:python}/mypy-cache" +PYTEST_ADDOPTS = "-o cache_dir=.venv-py{matrix:python}/pytest-cache" +RUFF_CACHE_DIR = ".venv-py{matrix:python}/ruff-cache" + +[tool.hatch.envs.test.overrides] +matrix.python.path = [ + # Sadly no way to interpolate this. + { value = ".venv-py3.10", if = ["3.10" ]}, + { value = ".venv-py3.11", if = ["3.11" ]}, + { value = ".venv-py3.12", if = ["3.12" ]}, + { value = ".venv-py3.13", if = ["3.13" ]}, + { value = ".venv-py3.14", if = ["3.14" ]} +] + +[[tool.hatch.envs.test.matrix]] +python = [ + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", +] + [tool.pytest.ini_options] # TODO: remove `-p no:warnings` addopts = "--no-header -p no:warnings" -cache_dir = ".venv/pytest-cache" [tool.black] target-version = ["py310"] @@ -108,7 +140,6 @@ line-length = 120 skip-string-normalization = true [tool.ruff] -cache-dir = ".venv/ruff-cache" target-version = "py310" line-length = 120 @@ -267,10 +298,6 @@ py-version = "3.10" # source root. # source-roots = -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode = true - # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. # unsafe-load-any-extension = @@ -443,6 +470,7 @@ bad-functions = ["map", "input"] # Maximum number of arguments for function / method. max-args = 9 +max-positional-arguments = 9 # Maximum number of attributes for a class (see R0902). max-attributes = 11 diff --git a/src/databricks/labs/pytester/fixtures/baseline.py b/src/databricks/labs/pytester/fixtures/baseline.py index ead15c1..36f674f 100644 --- a/src/databricks/labs/pytester/fixtures/baseline.py +++ b/src/databricks/labs/pytester/fixtures/baseline.py @@ -60,11 +60,7 @@ def inner(k=16) -> str: T = TypeVar("T") -def factory( - name: str, - create: Callable[..., T], - remove: Callable[[T], None], -) -> Generator[Callable[..., T], None, None]: +def factory(name: str, create: Callable[..., T], remove: Callable[[T], None]) -> Generator[Callable[..., T]]: """ Factory function for creating fixtures. diff --git a/src/databricks/labs/pytester/fixtures/catalog.py b/src/databricks/labs/pytester/fixtures/catalog.py index 37656f4..c1b729b 100644 --- a/src/databricks/labs/pytester/fixtures/catalog.py +++ b/src/databricks/labs/pytester/fixtures/catalog.py @@ -52,7 +52,7 @@ def make_table( make_random, log_workspace_link, watchdog_remove_after, -) -> Generator[Callable[..., TableInfo], None, None]: +) -> Generator[Callable[..., TableInfo]]: """ Create a table and return its info. Remove it after the test. Returns instance of `databricks.sdk.service.catalog.TableInfo`. @@ -245,7 +245,7 @@ def remove(table_info: TableInfo): @fixture def make_schema( sql_backend, make_random, log_workspace_link, watchdog_remove_after -) -> Generator[Callable[..., SchemaInfo], None, None]: +) -> Generator[Callable[..., SchemaInfo]]: """ Create a schema and return its info. Remove it after the test. Returns instance of `databricks.sdk.service.catalog.SchemaInfo`. @@ -286,9 +286,7 @@ def remove(schema_info: SchemaInfo): @fixture -def make_catalog( - ws, make_random, watchdog_remove_after, log_workspace_link -) -> Generator[Callable[..., CatalogInfo], None, None]: +def make_catalog(ws, make_random, watchdog_remove_after, log_workspace_link) -> Generator[Callable[..., CatalogInfo]]: """ Create a catalog and return its info. Remove it after the test. Returns instance of `databricks.sdk.service.catalog.CatalogInfo`. @@ -322,13 +320,7 @@ def create(*, name: str | None = None) -> CatalogInfo: @fixture -def make_udf( - ws, - env_or_skip, - sql_backend, - make_schema, - make_random, -) -> Generator[Callable[..., FunctionInfo], None, None]: +def make_udf(ws, env_or_skip, sql_backend, make_schema, make_random) -> Generator[Callable[..., FunctionInfo]]: """ Create a UDF and return its info. Remove it after the test. Returns instance of `databricks.sdk.service.catalog.FunctionInfo`. @@ -394,7 +386,7 @@ def remove(udf_info: FunctionInfo): @fixture -def make_storage_credential(ws, watchdog_remove_after) -> Generator[Callable[..., StorageCredentialInfo], None, None]: +def make_storage_credential(ws, watchdog_remove_after) -> Generator[Callable[..., StorageCredentialInfo]]: """ Create a storage credential and return its info. Remove it after the test. Returns instance of `databricks.sdk.service.catalog.StorageCredentialInfo`. @@ -449,9 +441,7 @@ def remove(storage_credential: StorageCredentialInfo): @fixture -def make_volume( - ws, make_catalog, make_schema, make_random, log_workspace_link -) -> Generator[Callable[..., VolumeInfo], None, None]: +def make_volume(ws, make_catalog, make_schema, make_random, log_workspace_link) -> Generator[Callable[..., VolumeInfo]]: """ Create a volume and return its info. Remove it after the test. Returns instance of `databricks.sdk.service.catalog.VolumeInfo`. diff --git a/src/databricks/labs/pytester/fixtures/compute.py b/src/databricks/labs/pytester/fixtures/compute.py index 6ea90d6..cfefc31 100644 --- a/src/databricks/labs/pytester/fixtures/compute.py +++ b/src/databricks/labs/pytester/fixtures/compute.py @@ -31,7 +31,7 @@ def make_cluster_policy( make_random, log_workspace_link, watchdog_purge_suffix, -) -> Generator[Callable[..., CreatePolicyResponse], None, None]: +) -> Generator[Callable[..., CreatePolicyResponse]]: """ Create a Databricks cluster policy and clean it up after the test. Returns a function to create cluster policies, which returns `databricks.sdk.service.compute.CreatePolicyResponse` instance. @@ -65,7 +65,7 @@ def create(*, name: str | None = None, **kwargs) -> CreatePolicyResponse: @fixture def make_cluster( ws, make_random, log_workspace_link, watchdog_remove_after -) -> Generator[Callable[..., Wait[ClusterDetails]], None, None]: +) -> Generator[Callable[..., Wait[ClusterDetails]]]: """ Create a Databricks cluster, waits for it to start, and clean it up after the test. Returns a function to create clusters. You can get `cluster_id` attribute from the returned object. @@ -129,7 +129,7 @@ def make_instance_pool( make_random, log_workspace_link, watchdog_remove_after, -) -> Generator[Callable[..., CreateInstancePoolResponse], None, None]: +) -> Generator[Callable[..., CreateInstancePoolResponse]]: """ Create a Databricks instance pool and clean it up after the test. Returns a function to create instance pools. Use `instance_pool_id` attribute from the returned object to get an ID of the pool. @@ -171,7 +171,7 @@ def make_job( make_workspace_file, log_workspace_link, watchdog_remove_after, -) -> Generator[Callable[..., Job], None, None]: +) -> Generator[Callable[..., Job]]: """ Create a Databricks job and clean it up after the test. Returns a function to create jobs, that returns a `databricks.sdk.service.jobs.Job` instance. @@ -271,7 +271,7 @@ def make_pipeline( make_notebook, watchdog_remove_after, watchdog_purge_suffix, -) -> Generator[Callable[..., CreatePipelineResponse], None, None]: +) -> Generator[Callable[..., CreatePipelineResponse]]: """ Create Delta Live Table Pipeline and clean it up after the test. Returns a function to create pipelines. Results in a `databricks.sdk.service.pipelines.CreatePipelineResponse` instance. @@ -315,9 +315,7 @@ def create(**kwargs) -> CreatePipelineResponse: @fixture -def make_warehouse( - ws, make_random, watchdog_remove_after -) -> Generator[Callable[..., Wait[GetWarehouseResponse]], None, None]: +def make_warehouse(ws, make_random, watchdog_remove_after) -> Generator[Callable[..., Wait[GetWarehouseResponse]]]: """ Create a Databricks warehouse and clean it up after the test. Returns a function to create warehouses. diff --git a/src/databricks/labs/pytester/fixtures/iam.py b/src/databricks/labs/pytester/fixtures/iam.py index 6b8bb21..a5fd6c4 100644 --- a/src/databricks/labs/pytester/fixtures/iam.py +++ b/src/databricks/labs/pytester/fixtures/iam.py @@ -157,7 +157,7 @@ def _double_check_group_in_listing() -> None: def _make_group( name: str, cfg: Config, interface, make_random, watchdog_purge_suffix -) -> Generator[Callable[..., Group], None, None]: +) -> Generator[Callable[..., Group]]: _not_specified = object() @retried(on=[ResourceConflict], timeout=timedelta(seconds=30)) diff --git a/src/databricks/labs/pytester/fixtures/ml.py b/src/databricks/labs/pytester/fixtures/ml.py index f5c15f3..541d28a 100644 --- a/src/databricks/labs/pytester/fixtures/ml.py +++ b/src/databricks/labs/pytester/fixtures/ml.py @@ -29,7 +29,7 @@ def make_experiment( make_directory, log_workspace_link, watchdog_purge_suffix, -) -> Generator[Callable[..., CreateExperimentResponse], None, None]: +) -> Generator[Callable[..., CreateExperimentResponse]]: """ Returns a function to create Databricks Experiments and clean them up after the test. The function returns a `databricks.sdk.service.ml.CreateExperimentResponse` object. @@ -71,7 +71,7 @@ def create( @fixture -def make_model(ws, make_random, watchdog_remove_after) -> Generator[Callable[..., ModelDatabricks], None, None]: +def make_model(ws, make_random, watchdog_remove_after) -> Generator[Callable[..., ModelDatabricks]]: """ Returns a function to create Databricks Models and clean them up after the test. The function returns a `databricks.sdk.service.ml.GetModelResponse` object. diff --git a/src/databricks/labs/pytester/fixtures/permissions.py b/src/databricks/labs/pytester/fixtures/permissions.py index a10cc6e..a875096 100644 --- a/src/databricks/labs/pytester/fixtures/permissions.py +++ b/src/databricks/labs/pytester/fixtures/permissions.py @@ -1,3 +1,5 @@ +from typing import TypeVar + import pytest from databricks.sdk.errors import InvalidParameterValue from databricks.sdk.service import iam @@ -10,6 +12,16 @@ # pylint: disable=too-complex +T = TypeVar("T") + + +# TODO: Move this somewhere more useful. +def _not_none(value: T | None, *, msg: str = "missing value") -> T: + if value is None: + raise ValueError(msg) + return value + + class _PermissionsChange: def __init__(self, object_id: str, before: list[iam.AccessControlRequest], after: list[iam.AccessControlRequest]): self.object_id = object_id @@ -24,8 +36,10 @@ def _principal(acr: iam.AccessControlRequest) -> str: return f"group_name {acr.group_name}" return f"service_principal_name {acr.service_principal_name}" - def _list(self, acl: list[iam.AccessControlRequest]): - return ", ".join(f"{self._principal(_)} {_.permission_level.value}" for _ in acl) + def _list(self, acl: list[iam.AccessControlRequest]) -> str: + return ", ".join( + f"{self._principal(_)} {_not_none(_.permission_level, msg='missing permission level').value}" for _ in acl + ) def __repr__(self): return f"{self.object_id} [{self._list(self.before)}] -> [{self._list(self.after)}]" @@ -44,7 +58,9 @@ def _principal(acr: AccessControl) -> str: return f"group_name {acr.group_name}" def _list(self, acl: list[AccessControl]): - return ", ".join(f"{self._principal(_)} {_.permission_level.value}" for _ in acl) + return ", ".join( + f"{self._principal(_)} {_not_none(_.permission_level, msg='missing permission level').value}" for _ in acl + ) def __repr__(self): return f"{self.object_id} [{self._list(self.before)}] -> [{self._list(self.after)}]" diff --git a/src/databricks/labs/pytester/fixtures/redash.py b/src/databricks/labs/pytester/fixtures/redash.py index 2b56c53..71d865a 100644 --- a/src/databricks/labs/pytester/fixtures/redash.py +++ b/src/databricks/labs/pytester/fixtures/redash.py @@ -14,7 +14,7 @@ def make_query( make_random, log_workspace_link, watchdog_remove_after, -) -> Generator[Callable[..., LegacyQuery], None, None]: +) -> Generator[Callable[..., LegacyQuery]]: """ Create a query and remove it after the test is done. Returns the `databricks.sdk.service.sql.LegacyQuery` object. diff --git a/src/databricks/labs/pytester/fixtures/workspace.py b/src/databricks/labs/pytester/fixtures/workspace.py index 50704f5..ce52a11 100644 --- a/src/databricks/labs/pytester/fixtures/workspace.py +++ b/src/databricks/labs/pytester/fixtures/workspace.py @@ -18,7 +18,7 @@ @fixture -def make_notebook(ws, make_random, watchdog_purge_suffix) -> Generator[Callable[..., WorkspacePath], None, None]: +def make_notebook(ws, make_random, watchdog_purge_suffix) -> Generator[Callable[..., WorkspacePath]]: """ Returns a function to create Databricks Notebooks and clean them up after the test. The function returns [`os.PathLike` object](https://github.com/databrickslabs/blueprint?tab=readme-ov-file#python-native-pathlibpath-like-interfaces). @@ -75,7 +75,7 @@ def create( @fixture -def make_workspace_file(ws, make_random, watchdog_purge_suffix) -> Generator[Callable[..., WorkspacePath], None, None]: +def make_workspace_file(ws, make_random, watchdog_purge_suffix) -> Generator[Callable[..., WorkspacePath]]: """ Returns a function to create Databricks workspace file and clean up after the test. The function returns [`os.PathLike` object](https://github.com/databrickslabs/blueprint?tab=readme-ov-file#python-native-pathlibpath-like-interfaces). @@ -136,9 +136,7 @@ def create( @fixture -def make_directory( - ws: WorkspaceClient, make_random, watchdog_purge_suffix -) -> Generator[Callable[..., WorkspacePath], None, None]: +def make_directory(ws: WorkspaceClient, make_random, watchdog_purge_suffix) -> Generator[Callable[..., WorkspacePath]]: """ Returns a function to create Databricks Workspace Folders and clean them up after the test. The function returns [`os.PathLike` object](https://github.com/databrickslabs/blueprint?tab=readme-ov-file#python-native-pathlibpath-like-interfaces). @@ -169,7 +167,7 @@ def create(*, path: str | Path | None = None) -> WorkspacePath: @fixture -def make_repo(ws, make_random, watchdog_purge_suffix) -> Generator[Callable[..., RepoInfo], None, None]: +def make_repo(ws, make_random, watchdog_purge_suffix) -> Generator[Callable[..., RepoInfo]]: """ Returns a function to create Databricks Repos and clean them up after the test. The function returns a `databricks.sdk.service.workspace.RepoInfo` object.