Skip to content

Commit 61fa92f

Browse files
authored
Merge pull request #137 from octue/release/0.1.14
Release/0.1.14
2 parents eb0817b + cbf4c5e commit 61fa92f

File tree

17 files changed

+88
-83
lines changed

17 files changed

+88
-83
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Publish on merge of release/x.y.z into main
1+
name: Release the package on merge of release/x.y.z into main
22

33
# Only trigger when a pull request into main branch is closed.
44
on:

docs/source/deploying_services.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Automated deployment with Octue means:
1515

1616
All you need to enable automated deployments are the following files in your repository root:
1717

18-
* A ``requirements.txt`` file that includes ``octue>=0.1.13`` and the rest of your service's dependencies
18+
* A ``requirements.txt`` file that includes ``octue>=0.1.14`` and the rest of your service's dependencies
1919
* A ``twine.json`` file
2020
* A ``deployment_configuration.json`` file (optional)
2121

octue/deployment/google/Dockerfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ WORKDIR $PROJECT_ROOT
88

99
RUN apt-get update -y && apt-get install -y --fix-missing build-essential && rm -rf /var/lib/apt/lists/*
1010

11-
# This will cache bust if any of the requirements change.
12-
COPY requirements*.txt .
13-
COPY setup.* .
14-
1511
COPY . .
1612

1713
# Install requirements (supports requirements.txt, requirements-dev.txt, and setup.py; all will be run if all are present.)

octue/mixins/serialisable.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,36 @@ class Serialisable:
77
"""Mixin class to make resources serialisable to JSON.
88
99
Objects must have a `.logger` and a `.id` property
10-
1110
"""
1211

1312
_SERIALISE_FIELDS = None
1413
_EXCLUDE_SERIALISE_FIELDS = ("logger",)
1514

1615
def __init__(self, *args, **kwargs):
1716
"""Constructor for serialisable mixin"""
18-
# Ensure it passes construction arguments up the chain
1917
super().__init__(*args, **kwargs)
2018

19+
@classmethod
20+
def deserialise(cls, serialised_object):
21+
"""Deserialise the given JSON-serialised object.
22+
23+
:param dict serialised_object:
24+
:return any:
25+
"""
26+
return cls(**serialised_object)
27+
2128
def to_file(self, file_name, **kwargs):
22-
"""Write to a JSON file
29+
"""Write the object to a JSON file.
2330
24-
:parameter file_name: file to write to, including relative or absolute path and .json extension
25-
:type file_name: path-like
31+
:parameter str file_name: file to write to, including relative or absolute path and .json extension
32+
:return None:
2633
"""
2734
self.logger.debug("Writing %s %s to file %s", self.__class__.__name__, self.id, file_name)
2835
with open(file_name, "w") as fp:
2936
fp.write(self.serialise(**kwargs, to_string=True))
3037

3138
def serialise(self, to_string=False, **kwargs):
32-
"""Serialise into a primitive dict or JSON string
39+
"""Serialise into a primitive dict or JSON string.
3340
3441
Serialises all non-private and non-protected attributes except for 'logger', unless the subclass has a
3542
`_serialise_fields` tuple of the attribute names to serialise. For example:

octue/resources/datafile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def to_cloud(self, project_name, bucket_name, path_in_bucket):
204204
"hash_value": self.hash_value,
205205
"cluster": self.cluster,
206206
"sequence": self.sequence,
207-
"tags": self.tags.serialise(to_string=False),
207+
"tags": self.tags.serialise(),
208208
},
209209
)
210210

octue/resources/dataset.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from octue.mixins import Hashable, Identifiable, Loggable, Pathable, Serialisable, Taggable
1111
from octue.resources.datafile import Datafile
1212
from octue.resources.filter_containers import FilterSet
13+
from octue.resources.tag import TagSet
1314

1415

1516
module_logger = logging.getLogger(__name__)
@@ -27,6 +28,7 @@ class Dataset(Taggable, Serialisable, Pathable, Loggable, Identifiable, Hashable
2728

2829
_FILTERSET_ATTRIBUTE = "files"
2930
_ATTRIBUTES_TO_HASH = "files", "name", "tags"
31+
_SERIALISE_FIELDS = "files", "name", "tags", "hash_value", "id", "path"
3032

3133
def __init__(self, name=None, id=None, logger=None, path=None, path_from=None, tags=None, **kwargs):
3234
"""Construct a Dataset"""
@@ -84,7 +86,7 @@ def from_cloud(cls, project_name, bucket_name, path_to_dataset_directory):
8486
name=serialised_dataset["name"],
8587
hash_value=serialised_dataset["hash_value"],
8688
path=storage.path.generate_gs_path(bucket_name, path_to_dataset_directory),
87-
tags=json.loads(serialised_dataset["tags"]),
89+
tags=TagSet(serialised_dataset["tags"]),
8890
files=datafiles,
8991
)
9092

@@ -107,7 +109,6 @@ def to_cloud(self, project_name, bucket_name, output_directory):
107109

108110
serialised_dataset = self.serialise()
109111
serialised_dataset["files"] = sorted(files)
110-
del serialised_dataset["absolute_path"]
111112
del serialised_dataset["path"]
112113

113114
GoogleCloudStorageClient(project_name=project_name).upload_from_string(

octue/resources/filter_containers.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,9 @@
44
def _filter(instance, filter_name=None, filter_value=None):
55
"""Returns a new instance containing only the Filterables to which the given filter criteria apply.
66
7-
Say we want to filter by files whose extension equals "csv". We want to be able to do...
8-
9-
ds = DataSet(... initialise with csv files and other files)
10-
Example of chaining:
11-
12-
ds.filter('files__extension_equals', 'csv')
13-
is equvalent to
14-
ds.files.filter(extension__equals', 'csv')
15-
>>> FilterSet containing a set of datafiles
16-
17-
18-
ds.filter('files__extension_equals', 'csv')
19-
is equvalent to
20-
ds.files.filter(extension__equals', 'csv')
7+
:param str filter_name:
8+
:param any filter_value:
9+
:return octue.resources.filter_containers.FilterSet:
2110
"""
2211
return instance.__class__((item for item in instance if item.satisfies(filter_name, filter_value)))
2312

octue/resources/manifest.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Manifest(Pathable, Serialisable, Loggable, Identifiable, Hashable):
1717
(or leaving), a data service for an analysis at the configuration, input or output stage."""
1818

1919
_ATTRIBUTES_TO_HASH = "datasets", "keys"
20+
_SERIALISE_FIELDS = "datasets", "keys", "hash_value", "id", "name", "path"
2021

2122
def __init__(self, id=None, logger=None, path=None, datasets=None, keys=None, **kwargs):
2223
super().__init__(id=id, logger=logger, path=path)
@@ -43,20 +44,6 @@ def __init__(self, id=None, logger=None, path=None, datasets=None, keys=None, **
4344
self._instantiate_datasets(datasets, key_list)
4445
vars(self).update(**kwargs)
4546

46-
@classmethod
47-
def deserialise(cls, serialised_manifest, from_string=False):
48-
"""Deserialise a Manifest from a dictionary."""
49-
if from_string:
50-
serialised_manifest = json.loads(serialised_manifest)
51-
52-
return cls(
53-
name=serialised_manifest["name"],
54-
id=serialised_manifest["id"],
55-
datasets=serialised_manifest["datasets"],
56-
keys=serialised_manifest["keys"],
57-
path=serialised_manifest["path"],
58-
)
59-
6047
@classmethod
6148
def from_cloud(cls, project_name, bucket_name, path_to_manifest_file):
6249
"""Instantiate a Manifest from Google Cloud storage.
@@ -113,7 +100,6 @@ def to_cloud(self, project_name, bucket_name, path_to_manifest_file, store_datas
113100

114101
serialised_manifest = self.serialise()
115102
serialised_manifest["datasets"] = sorted(datasets)
116-
del serialised_manifest["absolute_path"]
117103
del serialised_manifest["path"]
118104

119105
GoogleCloudStorageClient(project_name=project_name).upload_from_string(

octue/resources/tag.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from functools import lru_cache
44

55
from octue.exceptions import InvalidTagException
6-
from octue.mixins import Filterable
7-
from octue.resources.filter_containers import FilterSet
6+
from octue.mixins import Filterable, Serialisable
7+
from octue.resources.filter_containers import FilterList, FilterSet
8+
from octue.utils.encoders import OctueJSONEncoder
89

910

1011
TAG_PATTERN = re.compile(r"^$|^[a-z0-9][a-z0-9:\-]*(?<![:-])$")
@@ -28,8 +29,11 @@ def name(self):
2829
@property
2930
@lru_cache(maxsize=1)
3031
def subtags(self):
31-
""" Return the subtags of the tag as a new TagSet (e.g. TagSet({'a', 'b', 'c'}) for the Tag('a:b:c'). """
32-
return TagSet({Tag(subtag_name) for subtag_name in (self.name.split(":"))})
32+
"""Return the subtags of the tag in order as a FilterList (e.g. FilterList(['a', 'b', 'c']) for Tag('a:b:c').
33+
34+
:return FilterList(Tag):
35+
"""
36+
return FilterList(Tag(subtag_name) for subtag_name in self.name.split(":"))
3337

3438
def __eq__(self, other):
3539
if isinstance(other, str):
@@ -85,7 +89,7 @@ def _clean(name):
8589
return cleaned_name
8690

8791

88-
class TagSet:
92+
class TagSet(Serialisable):
8993
""" Class to handle a set of tags as a string. """
9094

9195
_FILTERSET_ATTRIBUTE = "tags"
@@ -114,10 +118,6 @@ def __init__(self, tags=None, *args, **kwargs):
114118
"Tags must be expressed as a whitespace-delimited string or an iterable of strings or Tag instances."
115119
)
116120

117-
def __str__(self):
118-
""" Serialise tags to a sorted list string. """
119-
return self.serialise()
120-
121121
def __eq__(self, other):
122122
""" Does this TagSet have the same tags as another TagSet? """
123123
if not isinstance(other, TagSet):
@@ -161,10 +161,35 @@ def any_tag_contains(self, value):
161161
""" Return True if any of the tags contains value. """
162162
return any(value in tag for tag in self)
163163

164-
def serialise(self, to_string=True):
165-
""" Serialise tags to a sorted list string. """
166-
serialised_tags = sorted(tag.name for tag in self)
164+
def filter(self, filter_name=None, filter_value=None):
165+
"""Filter the tags with the given filter for the given value.
166+
167+
:param str filter_name:
168+
:param any filter_value:
169+
:return octue.resources.filter_containers.FilterSet:
170+
"""
171+
return self.tags.filter(filter_name=filter_name, filter_value=filter_value)
172+
173+
def serialise(self, to_string=False, **kwargs):
174+
"""Serialise to a sorted list of tag names.
175+
176+
:param bool to_string:
177+
:return list|str:
178+
"""
179+
string = json.dumps(
180+
sorted(tag.name for tag in self.tags), cls=OctueJSONEncoder, sort_keys=True, indent=4, **kwargs
181+
)
167182

168183
if to_string:
169-
return str(serialised_tags)
170-
return serialised_tags
184+
return string
185+
186+
return json.loads(string)
187+
188+
@classmethod
189+
def deserialise(cls, serialised_tagset):
190+
"""Deserialise from a sorted list of tag names.
191+
192+
:param list serialised_tagset:
193+
:return TagSet:
194+
"""
195+
return cls(tags=serialised_tagset)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
octue==0.1.13
1+
octue==0.1.14

0 commit comments

Comments
 (0)