5 |
102 |
103 |
104 |
159 |
--------------------------------------------------------------------------------
/tests/test_storage.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 |
4 | from machinable import Storage
5 |
6 |
7 | class CopyStorage(Storage):
8 | class Config:
9 | directory: str = ""
10 |
11 | def commit(self, interface) -> bool:
12 | directory = os.path.join(self.config.directory, interface.uuid)
13 | if not os.path.exists(directory):
14 | os.makedirs(directory)
15 | interface.to_directory(directory)
16 |
17 | def contains(self, uuid):
18 | return os.path.exists(os.path.join(self.config.directory, uuid))
19 |
20 | def retrieve(self, uuid, local_directory) -> bool:
21 | if not self.contains(uuid):
22 | return False
23 |
24 | shutil.copytree(
25 | os.path.join(self.config.directory, uuid),
26 | local_directory,
27 | dirs_exist_ok=True,
28 | )
29 |
30 | return True
31 |
32 |
33 | def test_storage(tmp_path):
34 | from machinable import Index, get
35 |
36 | primary = str(tmp_path / "primary")
37 | secondary = str(tmp_path / "secondary")
38 |
39 | i = Index(
40 | {"directory": primary, "database": str(tmp_path / "index.sqlite")}
41 | ).__enter__()
42 |
43 | st2 = CopyStorage({"directory": secondary}).__enter__()
44 | st1 = Storage().__enter__()
45 |
46 | project = get("machinable.project", "tests/samples/project").__enter__()
47 |
48 | interface1 = get("dummy").commit()
49 | interface2 = get("dummy", {"a": 5}).commit()
50 |
51 | assert os.path.exists(os.path.join(primary, interface1.uuid))
52 | assert os.path.exists(os.path.join(secondary, interface1.uuid))
53 |
54 | # delete primary source and reload from remote
55 | shutil.rmtree(primary)
56 | assert not os.path.exists(interface1.local_directory())
57 | assert not os.path.exists(interface2.local_directory())
58 | interface1_reload = get("dummy")
59 | interface1_reload.fetch()
60 | assert os.path.exists(interface1_reload.local_directory())
61 | assert not os.path.exists(interface2.local_directory())
62 | interface2_reload = get("dummy", {"a": 5})
63 | interface2_reload.fetch()
64 | assert os.path.exists(interface2.local_directory())
65 |
66 | project.__exit__()
67 | st1.__exit__()
68 | st2.__exit__()
69 | i.__exit__()
70 |
71 |
72 | def test_storage_upload_and_download(tmp_path):
73 | from machinable import Index, get
74 |
75 | primary = str(tmp_path / "primary")
76 | secondary = str(tmp_path / "secondary")
77 |
78 | i = Index(primary).__enter__()
79 | local = Index(str(tmp_path / "download"))
80 |
81 | storage = CopyStorage({"directory": secondary})
82 |
83 | project = get("machinable.project", "tests/samples/project").__enter__()
84 |
85 | interface1 = get("dummy").launch()
86 | interface2 = get("dummy", {"a": 5}, uses=interface1).launch()
87 |
88 | assert not os.path.exists(tmp_path / "secondary" / interface2.uuid)
89 | storage.upload(interface2)
90 | assert os.path.exists(tmp_path / "secondary" / interface1.uuid)
91 | assert os.path.exists(tmp_path / "secondary" / interface2.uuid)
92 |
93 | assert not local.find(interface2)
94 | with local:
95 | downloads = storage.download(interface2.uuid, related=False)
96 | assert len(downloads) == 1
97 | assert os.path.exists(local.local_directory(interface2.uuid))
98 | assert not os.path.exists(local.local_directory(interface1.uuid))
99 | assert local.find(interface2)
100 | assert not local.find(interface1)
101 |
102 | downloads = storage.download(interface2.uuid, related=True)
103 | assert len(downloads) == 2
104 | assert os.path.exists(local.local_directory(interface1.uuid))
105 | assert local.find(interface1)
106 |
107 | project.__exit__()
108 | i.__exit__()
109 |
--------------------------------------------------------------------------------
/docs/guide/element.md:
--------------------------------------------------------------------------------
1 | # Element
2 |
3 | At a basic level, machinable projects are regular Python projects consisting of *elements* - configurable classes that encapsulate parts of your code.
4 | For example, an element implementation of a dataset might look like this:
5 |
6 | ```python
7 | from machinable import Element # Element base class
8 |
9 | class MnistData(Element):
10 | """A dataset of handwritten characters"""
11 | Config = {
12 | "batch_size": 8,
13 | "name": "mnist"
14 | }
15 | ```
16 |
17 | When inheriting from