Skip to content

Commit

Permalink
For #46904, site wide tk-config-default2 (#172)
Browse files Browse the repository at this point in the history
Adds support for a None entity link during sub-entity searches in Mockgun and PEP8ed mockgun.py
  • Loading branch information
jfboismenu authored Mar 23, 2018
1 parent 21dc3a9 commit 4b5764b
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 32 deletions.
90 changes: 60 additions & 30 deletions shotgun_api3/lib/mockgun/mockgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@

import datetime

from ... import sg_timezone, ShotgunError
from ... import ShotgunError
from ...shotgun import _Config
from .errors import MockgunError
from .schema import SchemaFactory
Expand Down Expand Up @@ -220,7 +220,6 @@ def __init__(self,
###################################################################################################
# public API methods


def get_session_token(self):
return "bogus_session_token"

Expand All @@ -245,15 +244,16 @@ def schema_field_read(self, entity_type, field_name=None):
else:
return dict((k, v) for k, v in self._schema[entity_type].items() if k == field_name)


def find(self, entity_type, filters, fields=None, order=None, filter_operator=None, limit=0, retired_only=False, page=0):

def find(
self, entity_type, filters, fields=None, order=None, filter_operator=None,
limit=0, retired_only=False, page=0
):

self.finds += 1

self._validate_entity_type(entity_type)
# do not validate custom fields - this makes it hard to mock up a field quickly
#self._validate_entity_fields(entity_type, fields)
# self._validate_entity_fields(entity_type, fields)

# FIXME: This should be refactored so that we can use the complex filer
# style in nested filter operations.
Expand All @@ -271,10 +271,10 @@ def find(self, entity_type, filters, fields=None, order=None, filter_operator=No

if len(f["values"]) != 1:
# {'path': 'id', 'relation': 'in', 'values': [1,2,3]} --> ["id", "in", [1,2,3]]
resolved_filters.append([ f["path"], f["relation"], f["values"] ])
resolved_filters.append([f["path"], f["relation"], f["values"]])
else:
# {'path': 'id', 'relation': 'is', 'values': [3]} --> ["id", "is", 3]
resolved_filters.append([ f["path"], f["relation"], f["values"][0] ])
resolved_filters.append([f["path"], f["relation"], f["values"][0]])

else:
# traditiona style sg filters
Expand Down Expand Up @@ -315,9 +315,14 @@ def find(self, entity_type, filters, fields=None, order=None, filter_operator=No

return val


def find_one(self, entity_type, filters, fields=None, order=None, filter_operator=None, retired_only=False):
results = self.find(entity_type, filters, fields=fields, order=order, filter_operator=filter_operator, retired_only=retired_only)
def find_one(
self, entity_type, filters, fields=None, order=None, filter_operator=None,
retired_only=False
):
results = self.find(
entity_type, filters, fields=fields,
order=order, filter_operator=filter_operator, retired_only=retired_only
)
return results[0] if results else None

def batch(self, requests):
Expand Down Expand Up @@ -440,22 +445,44 @@ def _validate_entity_data(self, entity_type, data):

if field_info["data_type"]["value"] == "multi_entity":
if not isinstance(item, list):
raise ShotgunError("%s.%s is of type multi_entity, but data %s is not a list" % (entity_type, field, item))
raise ShotgunError(
"%s.%s is of type multi_entity, but data %s is not a list" %
(entity_type, field, item)
)
elif item and any(not isinstance(sub_item, dict) for sub_item in item):
raise ShotgunError("%s.%s is of type multi_entity, but data %s contains a non-dictionary" % (entity_type, field, item))
raise ShotgunError(
"%s.%s is of type multi_entity, but data %s contains a non-dictionary" %
(entity_type, field, item)
)
elif item and any("id" not in sub_item or "type" not in sub_item for sub_item in item):
raise ShotgunError("%s.%s is of type multi-entity, but an item in data %s does not contain 'type' and 'id'" % (entity_type, field, item))
elif item and any(sub_item["type"] not in field_info["properties"]["valid_types"]["value"] for sub_item in item):
raise ShotgunError("%s.%s is of multi-type entity, but an item in data %s has an invalid type (expected one of %s)" % (entity_type, field, item, field_info["properties"]["valid_types"]["value"]))

raise ShotgunError(
"%s.%s is of type multi-entity, but an item in data %s does not contain 'type' and 'id'" %
(entity_type, field, item)
)
elif item and any(
sub_item["type"] not in field_info["properties"]["valid_types"]["value"] for sub_item in item
):
raise ShotgunError(
"%s.%s is of multi-type entity, but an item in data %s has an invalid type (expected one of %s)"
% (entity_type, field, item, field_info["properties"]["valid_types"]["value"])
)

elif field_info["data_type"]["value"] == "entity":
if not isinstance(item, dict):
raise ShotgunError("%s.%s is of type entity, but data %s is not a dictionary" % (entity_type, field, item))
raise ShotgunError(
"%s.%s is of type entity, but data %s is not a dictionary" %
(entity_type, field, item)
)
elif "id" not in item or "type" not in item:
raise ShotgunError("%s.%s is of type entity, but data %s does not contain 'type' and 'id'" % (entity_type, field, item))
#elif item["type"] not in field_info["properties"]["valid_types"]["value"]:
# raise ShotgunError("%s.%s is of type entity, but data %s has an invalid type (expected one of %s)" % (entity_type, field, item, field_info["properties"]["valid_types"]["value"]))
raise ShotgunError(
"%s.%s is of type entity, but data %s does not contain 'type' and 'id'"
% (entity_type, field, item)
)
# elif item["type"] not in field_info["properties"]["valid_types"]["value"]:
# raise ShotgunError(
# "%s.%s is of type entity, but data %s has an invalid type (expected one of %s)" %
# (entity_type, field, item, field_info["properties"]["valid_types"]["value"])
# )

else:
try:
Expand All @@ -472,10 +499,16 @@ def _validate_entity_data(self, entity_type, data):
"status_list": basestring,
"url": dict}[sg_type]
except KeyError:
raise ShotgunError("Field %s.%s: Handling for Shotgun type %s is not implemented" % (entity_type, field, sg_type))
raise ShotgunError(
"Field %s.%s: Handling for Shotgun type %s is not implemented" %
(entity_type, field, sg_type)
)

if not isinstance(item, python_type):
raise ShotgunError("%s.%s is of type %s, but data %s is not of type %s" % (entity_type, field, type(item), sg_type, python_type))
raise ShotgunError(
"%s.%s is of type %s, but data %s is not of type %s" %
(entity_type, field, type(item), sg_type, python_type)
)

# TODO: add check for correct timezone

Expand Down Expand Up @@ -641,6 +674,9 @@ def _get_field_from_row(self, entity_type, row, field):
sub_field_value = self._get_field_from_row(entity_type2, entity, field3)
values.append(sub_field_value)
return values
# The field is not set, so return None.
elif field_value is None:
return None
# not multi entity, must be entity.
elif not isinstance(field_value, dict):
raise ShotgunError("Invalid deep query field %s.%s" % (entity_type, field))
Expand All @@ -652,7 +688,7 @@ def _get_field_from_row(self, entity_type, row, field):

# ok so looks like the value is an entity link
# e.g. db contains: {"sg_sequence": {"type":"Sequence", "id": 123 } }
linked_row = self._db[ field_value["type"] ][ field_value["id"] ]
linked_row = self._db[field_value["type"]][field_value["id"]]

return self._get_field_from_row(entity_type2, linked_row, field3)
else:
Expand Down Expand Up @@ -769,7 +805,6 @@ def _row_matches_filters(self, entity_type, row, filters, filter_operator, retir
else:
raise ShotgunError("%s is not a valid filter operator" % filter_operator)


def _update_row(self, entity_type, row, data):
for field in data:
field_type = self._get_field_type(entity_type, field)
Expand All @@ -780,11 +815,6 @@ def _update_row(self, entity_type, row, data):
else:
row[field] = data[field]


def _validate_entity_exists(self, entity_type, entity_id):
if entity_id not in self._db[entity_type]:
raise ShotgunError("No entity of type %s exists with id %s" % (entity_type, entity_id))




14 changes: 12 additions & 2 deletions tests/test_mockgun.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,12 @@ def setUp(self):
"""
self._mockgun = Mockgun("https://test.shotgunstudio.com", login="user", password="1234")

self._project_link = self._mockgun.create("Project", {"name": "project"})
self._project_link = self._mockgun.create("Project", {"name": "project", "archived": False})

# This entity will ensure that a populated link field will be comparable.
self._mockgun.create(
"PipelineConfiguration",
{"code": "with_project", "project": self._project_link}
{"code": "with_project", "project": self._project_link, }
)

# This entity will ensure that an unpopulated link field will be comparable.
Expand All @@ -180,6 +180,16 @@ def test_searching_for_initialized_entity_field(self):
items = self._mockgun.find("PipelineConfiguration", [["project", "is_not", self._project_link]])
self.assertEqual(len(items), 1)

def test_find_entity_with_none_link(self):
"""
Make sure that we can search for sub entity fields on entities that have the field not set.
"""
# The pipeline configuration without_project doesn't have the project field set, so we're expecting
# it to not be returned here.
items = self._mockgun.find("PipelineConfiguration", [["project.Project.archived", "is", False]])
self.assertEqual(len(items), 1)
self.assertEqual(items[0]["id"], self._project_link["id"])


class TestTextFieldOperators(TestBaseWithExceptionTests):
"""
Expand Down

0 comments on commit 4b5764b

Please sign in to comment.