Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable and configure importlinter #896

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ repos:
hooks:
- id: mypy
args: [--strict]

- repo: https://github.com/seddonym/import-linter
rev: v2.2
hooks:
- id: import-linter
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
Write the date in place of the "Unreleased" in the case a new version is released. -->
# Changelog

## Unreleased

### Changed

- The client utility `tree` was moved from `tiled.utils` to `tiled.client.utils`.
It remains, as before, re-imported for convenience in `tiled.client`.
- The objects in `tiled.server.schemas` were merged into `tiled.schemas` and
the former was removed.

## 0.1.0-b19 (2024-02-19)

### Maintenance
Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorials/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ a directory of files or hierarchical structure like an HDF5 file or XML file.
Tiled provides a utility for visualizing a nested structure.

```python
>>> from tiled.utils import tree
>>> from tiled.client import tree
>>> tree(client)
├── big_image
├── small_image
Expand Down
23 changes: 23 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,26 @@ exclude = '''
| web-frontend
)
'''

[tool.importlinter]
root_package = "tiled"

[[tool.importlinter.contracts]]
name="Server cannot import from Client"
type="forbidden"
source_modules=["tiled.server"]
forbidden_modules=["tiled.client"]

[[tool.importlinter.contracts]]
name="Client cannot import from Server"
type="forbidden"
source_modules=["tiled.client"]
forbidden_modules=["tiled.server"]
# These exemptions allow for client to launch server on a background thread
# These imports are deferred, in local scope not global scope, and only are
# trigged when an in-process server in launched. The importlinter is not
# fine-grained enough to specify this.
ignore_imports=[
"tiled.client.constructors -> tiled.server.app",
"tiled.client.context -> tiled.server.settings",
]
2 changes: 1 addition & 1 deletion tiled/_tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
from ..client import Context, from_context
from ..client.xarray import write_xarray_dataset
from ..queries import Eq, Key
from ..schemas import Asset, DataSource, Management
from ..server.app import build_app, build_app_from_config
from ..server.schemas import Asset, DataSource, Management
from ..structures.core import StructureFamily
from ..utils import ensure_uri
from .utils import enter_username_password
Expand Down
2 changes: 1 addition & 1 deletion tiled/_tests/test_hdf5.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from ..adapters.hdf5 import HDF5Adapter
from ..adapters.mapping import MapAdapter
from ..client import Context, from_context, record_history
from ..client import tree as tree_util
from ..server.app import build_app
from ..utils import tree as tree_util


@pytest.fixture
Expand Down
2 changes: 1 addition & 1 deletion tiled/_tests/test_protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
SparseAdapter,
TableAdapter,
)
from tiled.server.schemas import Principal, PrincipalType
from tiled.schemas import Principal, PrincipalType
from tiled.structures.array import ArrayStructure, BuiltinDtype
from tiled.structures.awkward import AwkwardStructure
from tiled.structures.core import Spec, StructureFamily
Expand Down
2 changes: 1 addition & 1 deletion tiled/adapters/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
StructureFamilyQuery,
)
from ..query_registration import QueryTranslationRegistry
from ..server.schemas import SortingItem
from ..schemas import SortingItem
from ..structures.core import Spec, StructureFamily
from ..structures.table import TableStructure
from ..type_aliases import JSON
Expand Down
2 changes: 1 addition & 1 deletion tiled/adapters/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sparse
from numpy.typing import NDArray

from ..server.schemas import Principal
from ..schemas import Principal
from ..structures.array import ArrayStructure
from ..structures.awkward import AwkwardStructure
from ..structures.core import Spec, StructureFamily
Expand Down
2 changes: 1 addition & 1 deletion tiled/authn_database/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from sqlalchemy.sql import func
from sqlalchemy.types import TypeDecorator

from ..server.schemas import PrincipalType
from ..schemas import PrincipalType
from .base import Base

# Use JSON with SQLite and JSONB with PostgreSQL.
Expand Down
2 changes: 1 addition & 1 deletion tiled/catalog/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
ZARR_MIMETYPE,
)
from ..query_registration import QueryTranslationRegistry
from ..server.schemas import Asset, DataSource, Management, Revision, Spec
from ..schemas import Asset, DataSource, Management, Revision, Spec
from ..structures.core import StructureFamily
from ..structures.data_source import Storage
from ..utils import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from tiled.catalog.orm import JSONVariant
from tiled.catalog.utils import compute_structure_id
from tiled.server.schemas import Management
from tiled.schemas import Management

# revision identifiers, used by Alembic.
revision = "2ca16566d692"
Expand Down
2 changes: 1 addition & 1 deletion tiled/catalog/orm.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from sqlalchemy.schema import UniqueConstraint
from sqlalchemy.sql import func

from ..server.schemas import Management
from ..schemas import Management
from ..structures.core import StructureFamily
from .base import Base

Expand Down
2 changes: 1 addition & 1 deletion tiled/client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from ..utils import tree
from .constructors import from_context, from_profile, from_uri
from .container import ASCENDING, DESCENDING
from .context import Context
from .logger import hide_logs, record_history, show_logs
from .metadata_update import DELETE_KEY
from .utils import tree

__all__ = [
"ASCENDING",
Expand Down
105 changes: 105 additions & 0 deletions tiled/client/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import builtins
import collections
import uuid
from collections.abc import Hashable
from pathlib import Path
Expand Down Expand Up @@ -313,3 +314,107 @@ def get_asset_filepaths(node):
# because it cannot provide a filepath.
filepaths.append(path_from_uri(asset.data_uri))
return filepaths


def _line(nodes, last):
"Generate a single line for the tree utility"
tee = "├"
vertical = "│ "
horizontal = "── "
L = "└"
blank = " "
indent = ""
for item in last[:-1]:
if item:
indent += blank
else:
indent += vertical
if last[-1]:
return indent + L + horizontal + nodes[-1]
else:
return indent + tee + horizontal + nodes[-1]


def walk(tree, nodes=None):
"Walk the entries in a (nested) Tree depth first."
if nodes is None:
for node in tree:
yield from walk(tree, [node])
else:
value = tree[nodes[-1]]
if hasattr(value, "items"):
yield nodes
for k, v in value.items():
yield from walk(value, nodes + [k])
else:
yield nodes


def gen_tree(tree, nodes=None, last=None):
"A generator of lines for the tree utility"

# Normally, traversing a Tree will cause the structure clients to be
# instanitated which in turn triggers import of the associated libraries like
# numpy, pandas, and xarray. We want to avoid paying for that, especially
# when this function is used in a CLI where import overhead can accumulate to
# about 2 seconds, the bulk of the time. Therefore, we do something a bit
# "clever" here to override the normal structure clients with dummy placeholders.
from .container import Container

def dummy_client(*args, **kwargs):
return None

structure_clients = collections.defaultdict(lambda: dummy_client)
structure_clients["container"] = Container
fast_tree = tree.new_variation(structure_clients=structure_clients)
if nodes is None:
last_index = len(fast_tree) - 1
for index, node in enumerate(fast_tree):
yield from gen_tree(fast_tree, [node], [index == last_index])
else:
value = fast_tree[nodes[-1]]
if hasattr(value, "items"):
yield _line(nodes, last)
last_index = len(value) - 1
for index, (k, v) in enumerate(value.items()):
yield from gen_tree(value, nodes + [k], last + [index == last_index])
else:
yield _line(nodes, last)


def tree(tree, max_lines=20):
"""
Print a visual sketch of Tree structure akin to UNIX `tree`.

Parameters
----------
tree : Tree
max_lines: int or None, optional
By default, output is trucated at 20 lines. ``None`` means "Do not
truncate."

Examples
--------

>>> tree(tree)
├── A
│ ├── dog
│ ├── cat
│ └── monkey
└── B
├── snake
├── bear
└── wolf

"""
if len(tree) == 0:
print("<Empty>")
return
for counter, line in enumerate(gen_tree(tree), start=1):
if (max_lines is not None) and (counter > max_lines):
print(
f"<Output truncated at {max_lines} lines. "
"Adjust tree's max_lines parameter to see more.>"
)
break
print(line)
Loading
Loading