Skip to content

Commit 9eb8c57

Browse files
committed
added by_name loader with LRU cache
added lru_caching to by-name lookups global accessor works, but namespaces still a problem cache works for different namespaces, all tests pass changed _helpers to helpers
1 parent 2a05ff7 commit 9eb8c57

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+919
-888
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,18 @@ experiments = client.experiments
1717
exp = cellengine.Experiment(name="160311-96plex-4dye")
1818
```
1919

20+
##Developer Notes
21+
- `id` is a python builtin, which causes some confusion. We use `_id` to indicate
22+
the ID of an API object, but the `attrs` package does not accept leading
23+
underscores as a keyword argument. In other words, an init arg `_id = attr.ib()` in a
24+
class must be passed to instantiate that class as `id`. In every other case,
25+
the variable `_id` should be used. Practically, this means:
26+
- pass `_id` to functions that take an ID as an argument. This is the only
27+
situation a user should encounter.
28+
- pass `id` instead of `_id` to `attrs` classes when instantiating them.
29+
This should only happen inside the SDK; a user should never have to pass
30+
`id` instead of `_id`.
31+
- pass `properties` instead of `_properties` to `attrs` classes when
32+
instantiating them. A user should probably never have to pass
33+
`_properties` at all; this should be managed behind the scenes.
2034

cellengine/Gates/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .gate_util import delete_gates, get_fcsfile
1+
from .gate_util import delete_gates
22
from .rectangle_gate import create_rectangle_gate
33
from .polygon_gate import create_polygon_gate
44
from .ellipse_gate import create_ellipse_gate

cellengine/Gates/ellipse_gate.py

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
from custom_inherit import doc_inherit
2-
from .gate_util import common_gate_create, gate_style
3-
from .. import _helpers
4-
5-
@doc_inherit(common_gate_create, style=gate_style)
6-
def create_ellipse_gate(experiment_id, x_channel, y_channel, name,
7-
x, y, angle, major, minor, label=[], gid=None, locked=False,
8-
parent_population_id=None, parent_population=None,
9-
tailored_per_file=False, fcs_file_id=None,
10-
fcs_file=None, create_population=True):
2+
from .gate_util import create_common_gate, gate_style
3+
from .. import helpers
4+
5+
6+
@doc_inherit(create_common_gate, style=gate_style)
7+
def create_ellipse_gate(
8+
experiment_id,
9+
x_channel,
10+
y_channel,
11+
name,
12+
x,
13+
y,
14+
angle,
15+
major,
16+
minor,
17+
label=[],
18+
gid=None,
19+
locked=False,
20+
parent_population_id=None,
21+
parent_population=None,
22+
tailored_per_file=False,
23+
fcs_file_id=None,
24+
fcs_file=None,
25+
create_population=True,
26+
):
1127
"""Creates an ellipse gate.
1228
1329
Args:
@@ -30,25 +46,30 @@ def create_ellipse_gate(experiment_id, x_channel, y_channel, name,
3046
if label == []:
3147
label = [x, y]
3248
if gid is None:
33-
gid = _helpers.generate_id()
49+
gid = helpers.generate_id()
3450

3551
model = {
36-
'locked': locked,
37-
'label': label,
38-
'ellipse': {'angle': angle, 'major': major, 'minor': minor, 'center': [x, y]}
52+
"locked": locked,
53+
"label": label,
54+
"ellipse": {"angle": angle, "major": major, "minor": minor, "center": [x, y]},
3955
}
4056

4157
body = {
42-
'experimentId': experiment_id,
43-
'name': name,
44-
'type': 'EllipseGate',
45-
'gid': gid,
46-
'xChannel': x_channel,
47-
'yChannel': y_channel,
48-
'parentPopulationId': parent_population_id,
49-
'model': model
58+
"experimentId": experiment_id,
59+
"name": name,
60+
"type": "EllipseGate",
61+
"gid": gid,
62+
"xChannel": x_channel,
63+
"yChannel": y_channel,
64+
"parentPopulationId": parent_population_id,
65+
"model": model,
5066
}
5167

52-
return common_gate_create(experiment_id, body=body,
53-
tailored_per_file=tailored_per_file, fcs_file_id=fcs_file_id,
54-
fcs_file=fcs_file, create_population=create_population)
68+
return create_common_gate(
69+
experiment_id,
70+
body=body,
71+
tailored_per_file=tailored_per_file,
72+
fcs_file_id=fcs_file_id,
73+
fcs_file=fcs_file,
74+
create_population=create_population,
75+
)

cellengine/Gates/gate_util.py

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
from cellengine import loader
12
import re
2-
3-
from ..fcsfile import FcsFile
43
from .. import gate # circular import here
5-
from .. import _helpers
4+
from .. import helpers
5+
6+
cellengine = __import__(__name__.split(".")[0])
67

78

8-
def common_gate_create(
9+
def create_common_gate(
910
experiment_id, body, tailored_per_file, fcs_file_id, fcs_file, create_population
1011
):
1112
"""
@@ -51,8 +52,8 @@ def common_gate_create(
5152
experiment_id, body, tailored_per_file, fcs_file_id, fcs_file
5253
)
5354

54-
body = _helpers.convert_dict(body, "snake_to_camel")
55-
res = _helpers.base_create(
55+
body = helpers.convert_dict(body, "snake_to_camel")
56+
res = helpers.base_create(
5657
gate.Gate,
5758
url="experiments/{0}/gates".format(experiment_id),
5859
expected_status=201,
@@ -68,24 +69,13 @@ def parse_fcs_file_args(experiment_id, body, tailored_per_file, fcs_file_id, fcs
6869
if fcs_file is not None and fcs_file_id is not None:
6970
raise ValueError("Please specify only 'fcs_file' or 'fcs_file_id'.")
7071
if fcs_file is not None and tailored_per_file is True: # lookup by name
71-
_file = get_fcsfile(experiment_id, name=fcs_file)
72+
_file = loader.get_fcsfile(experiment_id, name=fcs_file)
7273
fcs_file_id = _file._id
7374
body["tailoredPerFile"] = tailored_per_file
7475
body["fcsFileId"] = fcs_file_id
7576
return body
7677

7778

78-
def get_fcsfile(experiment_id, _id=None, name=None):
79-
if _id:
80-
content = _helpers.base_get(
81-
"experiments/{0}/fcsfiles/{1}".format(experiment_id, _id)
82-
)
83-
content = FcsFile(properties=content)
84-
else:
85-
content = _helpers.load_fcsfile_by_name(experiment_id, name)
86-
return content
87-
88-
8979
def create_gates(experiment_id=None, gates=None, create_all_populations=True):
9080
"""Create multiple gates.
9181
@@ -112,8 +102,10 @@ def create_gates(experiment_id=None, gates=None, create_all_populations=True):
112102
``help(cellengine.Gate.<Gate Type>)``.
113103
fcs_file_id (optional): ``str``: ID of FCS file, if tailored per file. Use
114104
``None`` for the global gate in the tailored gate group.
115-
tailored_per_file (optional): ``bool``: Whether this gate is tailored per FCS file.
116-
names (optional): ``list(str)``: For compound gates, a list of gate names.
105+
tailored_per_file (optional): ``bool``: Whether this gate is
106+
tailored per FCS file.
107+
names (optional): ``list(str)``: For
108+
compound gates, a list of gate names.
117109
create_population (optional): Whether to create populations for each gate.
118110
create_populations: Whether to create populations for all gates. If set
119111
to False, ``create_population`` may be specified for each gate.
@@ -123,9 +115,9 @@ def create_gates(experiment_id=None, gates=None, create_all_populations=True):
123115
"""
124116
prepared_gates = []
125117
for g in gates:
126-
g.update({"_id": _helpers.generate_id()})
118+
g.update({"_id": helpers.generate_id()})
127119
if "gid" not in g.keys():
128-
g.update({"gid": _helpers.generate_id()})
120+
g.update({"gid": helpers.generate_id()})
129121
prepared_gates.append(g)
130122

131123
new_gates = []
@@ -134,7 +126,7 @@ def create_gates(experiment_id=None, gates=None, create_all_populations=True):
134126
create_populations = each_gate.get("create_population", False)
135127
else:
136128
create_populations = create_all_populations
137-
new_gate = common_gate_create(
129+
new_gate = create_common_gate(
138130
experiment_id=each_gate.get("experiment_id", experiment_id),
139131
body=each_gate,
140132
tailored_per_file=each_gate.get("tailored_per_file", False),
@@ -177,7 +169,7 @@ def delete_gates(experiment_id, _id=None, gid=None, exclude=None):
177169
if exclude:
178170
url = "{0}%exclude={1}".format(url, exclude)
179171

180-
_helpers.base_delete(url)
172+
helpers.base_delete(url)
181173

182174

183175
def gate_style(prnt_doc, child_doc):

cellengine/Gates/polygon_gate.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import numpy
22
from custom_inherit import doc_inherit
3-
from .gate_util import common_gate_create, gate_style
4-
from .. import _helpers
3+
from .gate_util import create_common_gate, gate_style
4+
from .. import helpers
55

66

7-
@doc_inherit(common_gate_create, style=gate_style)
7+
@doc_inherit(create_common_gate, style=gate_style)
88
def create_polygon_gate(experiment_id, x_channel, y_channel, name,
99
x_vertices, y_vertices, label=[], gid=None, locked=False,
1010
parent_population_id=None, parent_population=None,
@@ -29,7 +29,7 @@ def create_polygon_gate(experiment_id, x_channel, y_channel, name,
2929
if label == []:
3030
label = [numpy.mean(x_vertices), numpy.mean(y_vertices)]
3131
if gid is None:
32-
gid = _helpers.generate_id()
32+
gid = helpers.generate_id()
3333

3434
model = {
3535
'locked': locked,
@@ -48,7 +48,7 @@ def create_polygon_gate(experiment_id, x_channel, y_channel, name,
4848
'model': model
4949
}
5050

51-
return common_gate_create(experiment_id, body=body,
51+
return create_common_gate(experiment_id, body=body,
5252
tailored_per_file=tailored_per_file,
5353
fcs_file_id=fcs_file_id, fcs_file=fcs_file,
5454
create_population=create_population)

cellengine/Gates/quadrant_gate.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from math import pi
22
from custom_inherit import doc_inherit
3-
from .. import _helpers
4-
from .gate_util import common_gate_create, gate_style
3+
from .. import helpers
4+
from .gate_util import create_common_gate, gate_style
55

66

7-
@doc_inherit(common_gate_create, style=gate_style)
7+
@doc_inherit(create_common_gate, style=gate_style)
88
def create_quadrant_gate(experiment_id, x_channel, y_channel, name,
99
x, y, labels=[], gid=None, gids=None, locked=False,
1010
parent_population_id=None, parent_population=None,
@@ -38,7 +38,7 @@ def create_quadrant_gate(experiment_id, x_channel, y_channel, name,
3838
angles = [pi/2, pi, 3/2*pi, 0.000000]
3939

4040
# set labels based on axis scale
41-
r = _helpers.base_get(f'experiments/{experiment_id}/scalesets')[0]
41+
r = helpers.base_get(f'experiments/{experiment_id}/scalesets')[0]
4242
scale_min = min(x['scale']['minimum'] for x in r['scales'])
4343
scale_max = max(x['scale']['minimum'] for x in r['scales'])
4444

@@ -54,10 +54,10 @@ def create_quadrant_gate(experiment_id, x_channel, y_channel, name,
5454
raise ValueError('Labels must be a list of four length-2 lists.')
5555

5656
if gid is None:
57-
gid = _helpers.generate_id()
57+
gid = helpers.generate_id()
5858
if gids is None:
59-
gids = [_helpers.generate_id(), _helpers.generate_id(),
60-
_helpers.generate_id(), _helpers.generate_id()]
59+
gids = [helpers.generate_id(), helpers.generate_id(),
60+
helpers.generate_id(), helpers.generate_id()]
6161

6262
names = [name + append for append in [" (UR)", " (UL)", " (LL)", " (LR)"]]
6363

@@ -80,7 +80,7 @@ def create_quadrant_gate(experiment_id, x_channel, y_channel, name,
8080
'model': model
8181
}
8282

83-
return common_gate_create(experiment_id, body=body,
83+
return create_common_gate(experiment_id, body=body,
8484
tailored_per_file=tailored_per_file,
8585
fcs_file_id=fcs_file_id,
8686
fcs_file=fcs_file, create_population=create_population)

cellengine/Gates/range_gate.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import numpy
22
from custom_inherit import doc_inherit
3-
from .. import _helpers
4-
from .gate_util import common_gate_create, gate_style
3+
from .. import helpers
4+
from .gate_util import create_common_gate, gate_style
55

66

7-
@doc_inherit(common_gate_create, style=gate_style)
7+
@doc_inherit(create_common_gate, style=gate_style)
88
def create_range_gate(experiment_id, x_channel, name,
99
x1, x2, y=0.5, label=[], gid=None, locked=False,
1010
parent_population_id=None, parent_population=None,
@@ -32,7 +32,7 @@ def create_range_gate(experiment_id, x_channel, name,
3232
if label == []:
3333
label = [numpy.mean([x1, x2]), y]
3434
if gid is None:
35-
gid = _helpers.generate_id()
35+
gid = helpers.generate_id()
3636

3737
model = {
3838
'locked': locked,
@@ -50,7 +50,7 @@ def create_range_gate(experiment_id, x_channel, name,
5050
'model': model
5151
}
5252

53-
return common_gate_create(experiment_id, body=body,
53+
return create_common_gate(experiment_id, body=body,
5454
tailored_per_file=tailored_per_file,
5555
fcs_file_id=fcs_file_id,
5656
fcs_file=fcs_file, create_population=create_population)

cellengine/Gates/rectangle_gate.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import numpy
22
from custom_inherit import doc_inherit
3-
from .. import _helpers
4-
from .gate_util import common_gate_create, gate_style
3+
from .. import helpers
4+
from .gate_util import create_common_gate, gate_style
55

66

7-
@doc_inherit(common_gate_create, style=gate_style)
7+
@doc_inherit(create_common_gate, style=gate_style)
88
def create_rectangle_gate(experiment_id, x_channel, y_channel, name,
99
x1, x2, y1, y2, label=[], gid=None, locked=False,
1010
parent_population_id=None, parent_population=None,
@@ -33,7 +33,7 @@ def create_rectangle_gate(experiment_id, x_channel, y_channel, name,
3333
if label == []:
3434
label = [numpy.mean([x1, x2]), numpy.mean([y1, y2])]
3535
if gid is None:
36-
gid = _helpers.generate_id()
36+
gid = helpers.generate_id()
3737

3838
model = {
3939
'locked': locked,
@@ -52,7 +52,7 @@ def create_rectangle_gate(experiment_id, x_channel, y_channel, name,
5252
'model': model
5353
}
5454

55-
return common_gate_create(experiment_id, body=body,
55+
return create_common_gate(experiment_id, body=body,
5656
tailored_per_file=tailored_per_file,
5757
fcs_file_id=fcs_file_id,
5858
fcs_file=fcs_file, create_population=create_population)

cellengine/Gates/split_gate.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from custom_inherit import doc_inherit
2-
from .. import _helpers
3-
from .gate_util import common_gate_create, gate_style
2+
from .. import helpers
3+
from .gate_util import create_common_gate, gate_style
44

55

6-
@doc_inherit(common_gate_create, style=gate_style)
6+
@doc_inherit(create_common_gate, style=gate_style)
77
def create_split_gate(experiment_id, x_channel, name,
88
x, y, labels=[], gid=None, gids=None, locked=False,
99
parent_population_id=None, parent_population=None,
@@ -31,7 +31,7 @@ def create_split_gate(experiment_id, x_channel, name,
3131
y=100000)
3232
"""
3333
# set labels based on axis scale
34-
r = _helpers.base_get(f'experiments/{experiment_id}/scalesets')[0]
34+
r = helpers.base_get(f'experiments/{experiment_id}/scalesets')[0]
3535
scale_min = min(x['scale']['minimum'] for x in r['scales'])
3636
scale_max = max(x['scale']['minimum'] for x in r['scales'])
3737

@@ -44,9 +44,9 @@ def create_split_gate(experiment_id, x_channel, name,
4444
raise ValueError('Labels must be a list of two length-2 lists.')
4545

4646
if gid is None:
47-
gid = _helpers.generate_id()
47+
gid = helpers.generate_id()
4848
if gids is None:
49-
gids = [_helpers.generate_id(), _helpers.generate_id()]
49+
gids = [helpers.generate_id(), helpers.generate_id()]
5050

5151
names = [name + ' (L)', name + ' (R)']
5252

@@ -67,7 +67,7 @@ def create_split_gate(experiment_id, x_channel, name,
6767
'model': model
6868
}
6969

70-
return common_gate_create(experiment_id, body=body,
70+
return create_common_gate(experiment_id, body=body,
7171
tailored_per_file=tailored_per_file,
7272
fcs_file_id=fcs_file_id,
7373
fcs_file=fcs_file, create_population=create_population)

0 commit comments

Comments
 (0)