Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged

Io #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bf57c10
project structure
LucaMarconato Jun 30, 2022
3dcb28a
Merge branch 'main' into io
LucaMarconato Jun 30, 2022
cfaebfe
rm pl
LucaMarconato Jun 30, 2022
c6961c8
wip for io from and to zarr
LucaMarconato Jul 1, 2022
2adc5da
passing tests
LucaMarconato Jul 27, 2022
dfd5ae3
io first implementation, supporting the merfish dataset
LucaMarconato Jul 29, 2022
aecc499
add coordinateTransform to points
giovp Aug 1, 2022
9ede2f8
Merge branch 'main' into io
giovp Aug 1, 2022
d9abb67
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
32dd6ba
Merge branch 'main' into io
giovp Aug 1, 2022
eef0c81
Merge branch 'io' into giovanni/io
giovp Aug 1, 2022
2ddd104
add constants
giovp Aug 1, 2022
dfce99c
update writer
giovp Aug 1, 2022
83c4d43
add format
giovp Aug 1, 2022
69632f9
add arraylike typoe
giovp Aug 1, 2022
26ece9d
finish writers
giovp Aug 1, 2022
d4ae860
add write pre-commit to be fixed
giovp Aug 1, 2022
2f894bc
add parse tables
giovp Aug 1, 2022
1e4bb58
fix tests
giovp Aug 4, 2022
524a24b
fix pre-commit
giovp Aug 4, 2022
9168f3f
fix pre-commits
giovp Aug 4, 2022
c560ab1
fix tests datasets and remove empty files
giovp Aug 4, 2022
1780a35
fix pre-comit reader
giovp Aug 4, 2022
46a6198
fix all pre-commit
giovp Aug 4, 2022
3e8ea40
remove shapes and allow for multiple tables
giovp Aug 4, 2022
5e49ec7
use pre-commit CI and update workflow
giovp Aug 4, 2022
a7f2bf4
update pre-commit bui8ld
giovp Aug 4, 2022
96b4cb8
remove 3.8
giovp Aug 4, 2022
fa4adb8
update tests
giovp Aug 4, 2022
8771ea1
minor renaming
LucaMarconato Aug 8, 2022
647f225
fix missing import in github action
LucaMarconato Aug 9, 2022
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
2 changes: 2 additions & 0 deletions .flake8
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,6 +38,8 @@ ignore =
E203
# format string does contain unindexed parameters
P101
RST
D
exclude = .git,__pycache__,build,docs/_build,dist
per-file-ignores =
tests/*: D
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/build.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
name: Check Build

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
14 changes: 0 additions & 14 deletions .github/workflows/pre-commit.yaml

This file was deleted.

17 changes: 1 addition & 16 deletions .github/workflows/test.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.10"]
python: ["3.9", "3.10"]
os: [ubuntu-latest]

env:
Expand DownExpand Up@@ -60,18 +60,3 @@ jobs:
CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }}
run: |
codecov --required --flags=unittests

package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install test dependencies
run: python -m pip install --upgrade pip wheel twine build
- name: Build package
run: python -m build
- name: Check package
run: twine check --strict dist/*.whl
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,3 +22,6 @@ __pycache__/
# IDEs
/.idea/
.vscode

# data
spatialdata-sandbox
5 changes: 2 additions & 3 deletions .pre-commit-config.yaml
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,8 @@ repos:
rev: v0.971
hooks:
- id: mypy
additional_dependencies:
[numpy==1.22.0, scipy, pandas, types-requests]
exclude: tests/|docs/
additional_dependencies: [numpy==1.22.0, types-requests]
exclude: tests/|docs/|temp/|spatialdata/_core/reader.py
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@

[![Tests][badge-tests]][link-tests]
[![Documentation][badge-docs]][link-docs]
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/scverse/spatialdata/main.svg)](https://results.pre-commit.ci/latest/github/scverse/spatialdata/main)

[badge-tests]: https://img.shields.io/github/workflow/status/scverse/spatialdata/Test/main
[link-tests]: https://github.com/scverse/spatialdata.git/actions/workflows/test.yml
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,7 +22,7 @@ version = "0.0.1.dev0"
requires-python = ">=3.8"
license = {file = "LICENSE"}
readme = "README.md"
dependencies = ["anndata"]
dependencies = ["anndata","numpy","xarray","zarr","ome_zarr"]

[project.optional-dependencies]
dev = [
Expand DownExpand Up@@ -54,11 +54,13 @@ omit = [
testpaths = ["tests"]
xfail_strict = true
addopts = [
"-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
# "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below
"--import-mode=importlib", # allow using test files with same name
]
# info on how to use this https://stackoverflow.com/questions/57925071/how-do-i-avoid-getting-deprecationwarning-from-inside-dependencies-with-pytest
filterwarnings = [
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
"ignore::DeprecationWarning"
# "ignore:.*U.*mode is deprecated:DeprecationWarning",
]

[tool.isort]
Expand Down
1 change: 1 addition & 0 deletions spatialdata/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,3 +3,4 @@
__version__ = version("spatialdata")

from ._core import SpatialData
from ._core.transform import Transform
Empty file.
11 changes: 11 additions & 0 deletions spatialdata/_constants/_constants.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
from enum import unique

from spatialdata._constants._enum import ModeEnum


@unique
class ShapesType(ModeEnum):
CIRCLES = "circles"
TRIANGLES = "triangles"
SQUARES = "squares"
HEXAGONS = "hexagons"
66 changes: 66 additions & 0 deletions spatialdata/_constants/_enum.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
from abc import ABC, ABCMeta
from enum import Enum, EnumMeta
from functools import wraps
from typing import Any, Callable, Dict, Tuple, Type


class PrettyEnum(Enum):
"""Enum with a pretty :meth:`__str__` and :meth:`__repr__`."""

@property
def v(self) -> Any:
"""Alias for :attr`value`."""
return self.value

def __repr__(self) -> str:
return f"{self.value!r}"

def __str__(self) -> str:
return f"{self.value!s}"


def _pretty_raise_enum(cls: Type["ErrorFormatterABC"], func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> "ErrorFormatterABC":
try:
return func(*args, **kwargs) # type: ignore[no-any-return]
except ValueError as e:
_cls, value, *_ = args
e.args = (cls._format(value),)
raise e

if not issubclass(cls, ErrorFormatterABC):
raise TypeError(f"Class `{cls}` must be subtype of `ErrorFormatterABC`.")
elif not len(cls.__members__): # type: ignore[attr-defined]
# empty enum, for class hierarchy
return func

return wrapper


class ABCEnumMeta(EnumMeta, ABCMeta):
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
if getattr(cls, "__error_format__", None) is None:
raise TypeError(f"Can't instantiate class `{cls.__name__}` " f"without `__error_format__` class attribute.")
return super().__call__(*args, **kwargs)

def __new__(cls, clsname: str, superclasses: Tuple[type], attributedict: Dict[str, Any]) -> "ABCEnumMeta":
res = super().__new__(cls, clsname, superclasses, attributedict) # type: ignore[arg-type]
res.__new__ = _pretty_raise_enum(res, res.__new__) # type: ignore[assignment,arg-type]
return res


class ErrorFormatterABC(ABC):
"""Mixin class that formats invalid value when constructing an enum."""

__error_format__ = "Invalid option `{0}` for `{1}`. Valid options are: `{2}`."

@classmethod
def _format(cls, value: Enum) -> str:
return cls.__error_format__.format(
value, cls.__name__, [m.value for m in cls.__members__.values()] # type: ignore[attr-defined]
)


class ModeEnum(str, ErrorFormatterABC, PrettyEnum, metaclass=ABCEnumMeta):
"""Enum which prints available values when invalid value has been passed."""
30 changes: 30 additions & 0 deletions spatialdata/_constants/_key.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
from typing import Any, Callable


class cprop:
def __init__(self, f: Callable[..., str]):
self.f = f

def __get__(self, obj: Any, owner: Any) -> str:
return self.f(owner)


class Key:
"""Class which manages keys in :class:`anndata.AnnData`."""

class obs:
pass

class obsm:
@cprop
def spatial(cls) -> str:
return "spatial"

class uns:
@cprop
def spatial(cls) -> str:
return Key.obsm.spatial

@classmethod
def nhood_enrichment(cls, cluster: str) -> str:
return f"{cluster}_nhood_enrichment"
2 changes: 1 addition & 1 deletion spatialdata/_core/__init__.py
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
from .spatialdata import SpatialData
from ._spatialdata import SpatialData

__all__ = ["SpatialData"]
Loading