Skip to content
Merged
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
16 changes: 9 additions & 7 deletions src/projspec/artifact/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ def make(self, *args, **kwargs):
self._make(*args, **kwargs)

def _make(self, *args, **kwargs):
subprocess.check_call(self.cmd, cwd=self.proj.url, **self.kw)
subprocess.check_call(self.cmd, cwd=self.proj.url, **kwargs)

def remake(self, reqs=False):
"""Recreate artifact and any runtime it depends on"""
"""Recreate the artifact and any runtime it depends on"""
if reqs:
self.clean_req()
self.clean()
Expand All @@ -82,12 +82,14 @@ def _repr2(self):

@classmethod
def __init_subclass__(cls, **kwargs):
registry[camel_to_snake(cls.__name__)] = cls
sn = cls.snake_name()
if sn in registry:
raise RuntimeError()
registry[sn] = cls

def to_json(self):
out = self.__dict__.copy()
out["cls"] = camel_to_snake(self.__name__)
return out
@classmethod
def snake_name(cls):
return camel_to_snake(cls.__name__)


def get_artifact_cls(name: str) -> type[BaseArtifact]:
Expand Down
9 changes: 8 additions & 1 deletion src/projspec/content/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ def _repr2(self):

@classmethod
def __init_subclass__(cls, **kwargs):
registry[camel_to_snake(cls.__name__)] = cls
sn = cls.snake_name()
if sn in registry:
raise RuntimeError()
registry[sn] = cls

@classmethod
def snake_name(cls):
return camel_to_snake(cls.__name__)


def get_content_cls(name: str) -> type[BaseContent]:
Expand Down
12 changes: 10 additions & 2 deletions src/projspec/proj/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def __init__(
fs, path = fsspec.url_to_fs(path, **(storage_options or {}))
self.fs = fs
self.url = path
self.specs = {}
self.children = {}
self.specs = AttrDict()
self.children = AttrDict()
self.excludes = excludes or default_excludes
self.resolve(walk=walk, types=types)

Expand Down Expand Up @@ -172,6 +172,10 @@ def __contains__(self, item) -> bool:
item in _ for _ in self.children.values()
)

def to_dict(self) -> dict:
dic = AttrDict(specs=self.specs, children=self.children)
return dic.to_dict()


class ProjectSpec:
"""A project specification
Expand Down Expand Up @@ -248,3 +252,7 @@ def __repr__(self):
if self.artifacts:
base += f"\nArtifacts:\n{yaml.dump(self.artifacts.to_dict(), Dumper=IndentDumper).rstrip()}\n"
return base

def to_dict(self) -> dict:
dic = AttrDict(contents=self.contents, artifacts=self.artifacts)
return dic.to_dict()
13 changes: 9 additions & 4 deletions src/projspec/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def __init__(self, *data, **kw):
if isinstance(data[0], dict):
super().__init__(data[0])
elif isinstance(data[0], list):
if len(types) > 1:
raise TypeError("Multiple types ina list")
super().__init__(
{camel_to_snake(next(iter(types)).__name__): data[0]}
)
Expand Down Expand Up @@ -53,8 +55,10 @@ def to_dict(obj):
from projspec.content import BaseContent

if isinstance(obj, dict):
# includes AttrDict
return {k: to_dict(v) for k, v in obj.items()}
return {
k: v.to_dict() if hasattr(v, "to_dict") else to_dict(v)
for k, v in obj.items()
}
if isinstance(obj, (bytes, str)):
return obj
if isinstance(obj, Iterable):
Expand Down Expand Up @@ -91,7 +95,7 @@ def _linked_local_path(path):


class IsInstalled:
"""Checks if we can call commands, as a function of current environment"""
"""Checks if we can call commands, as a function of the current environment."""

cache = {}

Expand Down Expand Up @@ -120,7 +124,8 @@ def exists(self, cmd: str, refresh=False):
return self.cache[(self.env, cmd)]

def __contains__(self, item):
# canonical use: `"python" in is_installed`
# canonical use:
# "python" in is_installed`
# shutil.which?
return self.exists(item)

Expand Down
31 changes: 30 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
from projspec.utils import is_installed
import pytest

from projspec.content import BaseContent
from projspec.content.metadata import DescriptiveMetadata
from projspec.utils import AttrDict, is_installed


def test_is_installed():
assert "python" in is_installed


def test_attrdict():
d = AttrDict({"a": 1, "b": 2, "c": 3})
assert d.a == 1
assert dict(d) == d

d2 = AttrDict(a=1, b=2, c=3)
assert d2 == d


def test_attrdict_entity():
d = AttrDict(
BaseContent(proj=None, artifacts=set()),
DescriptiveMetadata(proj=None, artifacts=set()),
)
assert set(d) == {"base_content", "descriptive_metadata"}

with pytest.raises(TypeError):
AttrDict(
[
BaseContent(proj=None, artifacts=set()),
DescriptiveMetadata(proj=None, artifacts=set()),
]
)