def setUp(self): super(TestValidateContext, self).setUp() self.setup_fixtures() self.app_name = "test_app" self.template_name = "template_name" self.config_name = "template_config_name" self.config = {self.config_name: self.template_name} # set up test data with single sequence and shot seq = {"type":"Sequence", "name":"seq_name", "id":3} seq_path = os.path.join(self.project_root, "sequence/Seq") self.add_production_path(seq_path, seq) shot = {"type":"Shot", "name": "shot_name", "id":2, "project": self.project} shot_path = os.path.join(seq_path, "shot_code") self.add_production_path(shot_path, shot) # a second shot path without sequence shot_path_2 = os.path.join(self.project_root, "shot_code") self.add_production_path(shot_path_2, shot) # setup context with values for project and shot self.context = self.tk.context_from_path(shot_path) # Template to metadata self.metadata = {self.config_name:{"type":"template", "required_fields":[]}} # keys for templates self.keys = {"Sequence": StringKey("Sequence"), "Shot": StringKey("Shot")}
def setUp(self): super(TestAsTemplateFields, self).setUp() # create a context obj using predefined data kws = {} kws["tk"] = self.tk kws["project"] = self.project kws["entity"] = self.shot kws["step"] = self.step self.ctx = context.Context(**kws) # create a template with which to filter self.keys = { "Sequence": StringKey("Sequence"), "Shot": StringKey("Shot"), "Step": StringKey("Step"), "static_key": StringKey("static_key"), "shotgun_field": StringKey("shotgun_field", shotgun_entity_type="Shot", shotgun_field_name="shotgun_field") } template_def = "/sequence/{Sequence}/{Shot}/{Step}/work" self.template = TemplatePath(template_def, self.keys, self.project_root)
def setUp(self): super(TestValidateSettings, self).setUp() # set up data so as to supply a valid context seq = {"type": "Sequence", "name": "seq_name", "id": 3} seq_path = os.path.join(self.project_root, "sequence/Seq") self.add_production_path(seq_path, seq) shot = { "type": "Shot", "name": "shot_name", "id": 2, "project": self.project } shot_path = os.path.join(seq_path, "shot_code") self.add_production_path(shot_path, shot) # a second shot path without sequence shot_path_2 = os.path.join(self.project_root, "shot_code") self.add_production_path(shot_path_2, shot) # setup context with values for project and shot self.tk = tank.Tank(self.project_root) self.context = self.tk.context_from_path(shot_path) # The validation code needs a name for error reporting self.app_name = "test_app" # keys for templates self.keys = { "Sequence": StringKey("Sequence"), "Shot": StringKey("Shot") }
def test_skip_invalid(self): """Test that files not valid for an template are not returned. This refers to bug reported in Ticket #17090 """ keys = { "Shot": StringKey("Shot"), "Sequence": StringKey("Sequence"), "Step": StringKey("Step"), "name": StringKey("name"), "version": IntegerKey("version", format_spec="03") } definition = "sequences/{Sequence}/{Shot}/{Step}/work/{name}.v{version}.nk" template = TemplatePath(definition, keys, self.project_root, "my_template") tk = tank.Tank(self.project_root) tk._templates = {template.name: template} bad_file_path = os.path.join(self.project_root, "sequences", "Sequence1", "Shot1", "Foot", "work", "name1.va.nk") good_file_path = os.path.join(self.project_root, "sequences", "Sequence1", "Shot1", "Foot", "work", "name.v001.nk") self.create_file(bad_file_path) self.create_file(good_file_path) ctx_fields = {"Sequence": "Sequence1", "Shot": "Shot1", "Step": "Foot"} result = tk.paths_from_template(template, ctx_fields) self.assertIn(good_file_path, result) self.assertNotIn(bad_file_path, result)
def test_subset_format(self): """ Test subset_format parameter """ if sys.version_info < (2, 6): # subset format not supported in py25 self.assertRaises(TankError, StringKey, "field_name", subset="(.{3}).*", subset_format="{0} FOO") return # test properties template_field = StringKey("field_name", subset="(.)().*", subset_format="{0} FOO") self.assertEquals("{0} FOO", template_field.subset_format) # cannot specify subset_format without subset self.assertRaises(TankError, StringKey, "field_name", subset_format="{0} FOO") tests = [] # basic test tests.append( { "short": "\u3042foo ", "full": "foobar", "template": StringKey("field_name", subset="(.{3}).*", subset_format="\u3042{0} ") } ) # unicode tests.append( { "short": u'\u3042\u308a\u304c ', "full": u'\u3042\u308a\u304c\u3068', "template": StringKey("field_name", subset="(.{3}).*", subset_format="{0} ") } ) # multi token tests.append( { "short": 'S J', "full": 'John Smith', "template": StringKey("field_name", subset='([A-Z])[a-z]* ([A-Z])[a-z]*', subset_format="{1} {0}") } ) for test in tests: print test short = test["short"] full = test["full"] template_field = test["template"] self.assertEquals(short, template_field.value_from_str(short)) self.assertEquals(full, template_field.value_from_str(full)) self.assertEquals(short, template_field.str_from_value(full)) self.assertTrue(template_field.validate(full)) self.assertFalse(template_field.validate(short[0])) self.assertRaises(TankError, template_field.str_from_value, short[0])
def test_context_missing_fields(self): """ Case that a template's fields(keys) that are not part of the metadata's required fields are have no value in the context. """ # template with fields not in required fields or context field_name = "field_2" self.keys[field_name] = StringKey(field_name) self.keys["sppk"] = StringKey("sppk") template = tank.template.TemplatePath( "{%s}{sppk}" % field_name, self.keys, self.project_root ) # tank instance with this template self.tk.templates = {self.template_name: template} expected_msg = ( "Context %s can not determine value for fields %s needed by template %s" % (self.context, ["sppk"], template) ) self.check_error_message( TankError, expected_msg, validate_settings, self.app_name, self.tk, self.context, self.metadata, self.config, )
def setUp(self): super(TestTemplateString, self).setUp() self.keys = { "Sequence": StringKey("Sequence"), "Shot": StringKey("Shot"), "version": IntegerKey("version"), } self.template_string = TemplateString("something-{Shot}.{Sequence}", self.keys)
def test_enum_after_ambiguous(self): keys = { "Asset": StringKey("Asset"), "name": StringKey("name", choices=["dagle", "doogle"]) } self.keys.update(keys) definition = "build/maya/{Asset}_{name}.ext" input_path = "build/maya/cat_man_doogle.ext" expected = {"Asset": "cat_man", "name": "doogle"} self.assert_path_matches(definition, input_path, expected)
def setUp(self): super(TestContext, self).setUp() self.setup_multi_root_fixtures() self.keys = {"Sequence": StringKey("Sequence"), "Shot": StringKey("Shot"), "Step": StringKey("Step"), "static_key": StringKey("static_key")} # set up test data with single sequence, shot, step and human user self.seq = {"type":"Sequence", "code":"seq_name", "id":3} self.shot = {"type":"Shot", "code": "shot_name", "id":2, "extra_field": "extravalue", # used to test query from template "sg_sequence": self.seq, "project": self.project} self.step = {"type":"Step", "name": "step_name", "id": 4} # One human user not matching the current login self.other_user = {"type":"HumanUser", "name":"user_name", "id":1, "login": "******"} # One human user matching the current login self.current_login = tank.util.login.get_login_name() self.current_user = {"type":"HumanUser", "name":"user_name", "id":2, "login": self.current_login} self.seq_path = os.path.join(self.project_root, "sequence/Seq") self.add_production_path(self.seq_path, self.seq) self.shot_path = os.path.join(self.seq_path, "shot_code") self.add_production_path(self.shot_path, self.shot) self.step_path = os.path.join(self.shot_path, "step_short_name") self.add_production_path(self.step_path, self.step) self.other_user_path = os.path.join(self.step_path, "user_login") self.add_production_path(self.other_user_path, self.other_user) # adding shot path with alternate root seq_path = os.path.join(self.alt_root_1, "sequence/Seq") self.add_production_path(seq_path, self.seq) self.alt_1_shot_path = os.path.join(seq_path, "shot_code") self.add_production_path(self.alt_1_shot_path, self.shot) self.alt_1_step_path = os.path.join(self.alt_1_shot_path, "step_short_name") self.add_production_path(self.alt_1_step_path, self.step) self.alt_1_other_user_path = os.path.join(self.alt_1_step_path, "user_login") self.add_production_path(self.alt_1_other_user_path, self.other_user) # adding a path with step as the root (step/sequence/shot) alt_2_step_path = "step_short_name" self.add_production_path(alt_2_step_path, self.step) alt_2_seq_path = os.path.join(alt_2_step_path, "Seq") self.add_production_path(alt_2_seq_path, self.seq) alt_2_shot_path = os.path.join(alt_2_seq_path, "shot_code") self.add_production_path(alt_2_shot_path, self.shot)
def test_list_field_step_above_entity(self): """ Case that list field, such as asset type, and step are above the entity. """ # Add asset paths for same step different asset_type asset_type = "Character" asset_code = "asset_code" step_short_name = "step_short_name" asset_1 = { "type": "Asset", "id": 1, "code": asset_code, "name": "asset_name", "project": self.project, "asset_type": asset_type } step_path = os.path.join(self.project_root, asset_type, step_short_name) self.add_production_path(step_path, self.step) asset_path = os.path.join(step_path, asset_code) self.add_production_path(asset_path, asset_1) # second asset with different asset type asset_type_2 = "Prop" asset_2 = { "type": "Asset", "id": 2, "code": "asset_code_2", "name": "asset_name_2", "project": self.project, "asset_type": asset_type_2 } alt_step_path = os.path.join(self.project_root, asset_type_2, step_short_name) self.add_production_path(alt_step_path, self.step) asset_2_path = os.path.join(alt_step_path, asset_2["code"]) ctx = self.tk.context_from_path(asset_path) # create template for this setup self.keys["asset_type"] = StringKey("asset_type") self.keys["Asset"] = StringKey("Asset") definition = "{asset_type}/{Step}/{Asset}/work" template = TemplatePath(definition, self.keys, self.project_root) result = ctx.as_template_fields(template) self.assertEquals(asset_type, result["asset_type"]) self.assertEquals(step_short_name, result["Step"]) self.assertEquals(asset_code, result["Asset"])
def setUp(self): super(TestPathsFromTemplateGlob, self).setUp() keys = {"Shot": StringKey("Shot"), "version": IntegerKey("version", format_spec="03"), "seq_num": SequenceKey("seq_num", format_spec="05")} self.template = TemplatePath("{Shot}/{version}/filename.{seq_num}", keys, root_path=self.project_root)
def test_bad_alphanumeric(self): """ Tests applying non-alphanumeric values to keys of type alphanumeric. """ # single key template key = StringKey("alpha_num", filter_by="alphanumeric") template = TemplatePath("{alpha_num}", {"alpha_num": key}, self.project_root) invalid_values = [ "_underscore", "white space", "@mpersand", "par(enthes)", "###", ] for invalid_value in invalid_values: expected_msg = ( "%s Illegal value '%s' does not fit filter_by 'alphanumeric'" % (str(key), invalid_value)) self.check_error_message( TankError, expected_msg, template.apply_fields, {"alpha_num": invalid_value}, )
def test_enum_ambigous(self): key = StringKey("Asset", choices=["cat_man", "dog_man"]) self.keys["Asset"] = key definition = "build/maya/{Asset}_{name}.ext" input_path = "build/maya/cat_man_doogle.ext" expected = {"Asset": "cat_man", "name": "doogle"} self.assert_path_matches(definition, input_path, expected)
def test_user_ctx(self): """Check other_user is set when contained in the path.""" # get a context containing a user ctx = self.tk.context_from_path(self.other_user_path) # check context's attributes self.assertEquals(self.shot["id"], ctx.entity["id"]) self.assertEquals(self.shot["type"], ctx.entity["type"]) self.assertEquals(self.project["id"], ctx.project["id"]) self.assertEquals(self.project["type"], ctx.project["type"]) self.assertEquals(self.step["id"], ctx.step["id"]) self.assertEquals(self.step["type"], ctx.step["type"]) self.assertEquals(self.other_user["id"], ctx.user["id"]) self.assertEquals(self.other_user["type"], ctx.user["type"]) self.assertIsNone(ctx.task) # create a template that uses user self.keys["HumanUser"] = StringKey("HumanUser") template_def = "/sequence/{Sequence}/{Shot}/{Step}/{HumanUser}" template = TemplatePath(template_def, self.keys, self.project_root) # pull out fields and test that we have everythign we expect fields = ctx.as_template_fields(template) self.assertEquals(fields["HumanUser"], "user_login") self.assertEquals(fields["Shot"], "shot_code") self.assertEquals(fields["Sequence"], "Seq") self.assertEquals(fields["Step"], "step_short_name") self.assertEquals(len(fields), 4)
def test_query_from_template(self): query_key = StringKey("shot_extra", shotgun_entity_type="Shot", shotgun_field_name="extra_field") self.keys["shot_extra"] = query_key # shot_extra cannot be gotten from path cache template_def = "/sequence/{Sequence}/{Shot}/{Step}/work/{shot_extra}.ext" template = TemplatePath(template_def, self.keys, self.project_root) result = self.ctx.as_template_fields(template) self.assertEquals("extravalue", result["shot_extra"])
def test_aliased_key(self): """Test template which uses aliased key in it's definition.""" keys = {} keys["old_name"] = StringKey("new_name") definition = "{old_name}/something" template = TemplatePath(definition, keys, root_path=self.project_root) result = template.parent self.assertEquals("{new_name}", result.definition)
def test_confilicting_key_names(self): """ Two keys used in the same definition, both with the same alias. """ alt_key = StringKey("Shot") self.keys["Alt_Shot"] = alt_key definition = "something/{Alt_Shot}/{Shot}" self.assertRaises(TankError, Template, definition, self.keys)
def test_skip_enum(self): expected = os.path.join(self.project_root, "*") key = StringKey("Shot", choices=["s1", "s2"]) template = TemplatePath("{Shot}", {"Shot": key}, self.project_root) fields = {"Shot": "*"} skip_fields = ["Shot"] result = template._apply_fields(fields, ignore_types=skip_fields) self.assertEquals(expected, result)
def setUp(self): super(TestTemplatePath, self).setUp() # Make various types of keys(fields) self.keys = { "Sequence": StringKey("Sequence"), "Shot": StringKey("Shot", default="s1", choices=["s1", "s2", "shot_1"]), "Step": StringKey("Step"), "branch": StringKey("branch", filter_by="alphanumeric"), "name": StringKey("name"), "name_alpha": StringKey("name_alpha", filter_by="alphanumeric"), "version": IntegerKey("version", format_spec="03"), "snapshot": IntegerKey("snapshot", format_spec="03"), "ext": StringKey("ext"), "seq_num": SequenceKey("seq_num"), "frame": SequenceKey("frame", format_spec="04") } # Make a template self.definition = "shots/{Sequence}/{Shot}/{Step}/work/{Shot}.{branch}.v{version}.{snapshot}.ma" self.template_path = TemplatePath(self.definition, self.keys, self.project_root) # make template with sequence key self.sequence = TemplatePath("/path/to/seq.{frame}.ext", self.keys, "", "frame")
def setUp(self): super(TestMakeTemplatePaths, self).setUp() self.keys = {"Shot": StringKey("Shot")} self.multi_os_data_roots = { "unit_tests": { "win32": os.path.join(self.tank_temp, "project_code"), "linux2": os.path.join(self.tank_temp, "project_code"), "darwin": os.path.join(self.tank_temp, "project_code") } }
def setUp(self): super(TestTemplatePath, self).setUp( parameters={"primary_root_name": "primary_with_a_different_name"}) # Make various types of keys(fields) self.keys = { "Sequence": StringKey("Sequence"), "Shot": StringKey("Shot", default="s1", choices=["s1", "s2", "shot_1"]), "Step": StringKey("Step"), "branch": StringKey("branch", filter_by="alphanumeric"), "name": StringKey("name"), "name_alpha": StringKey("name_alpha", filter_by="alphanumeric"), "version": IntegerKey("version", format_spec="03"), "snapshot": IntegerKey("snapshot", format_spec="03"), "ext": StringKey("ext"), "seq_num": SequenceKey("seq_num"), "frame": SequenceKey("frame", format_spec="04"), } # Make a template self.definition = "shots/{Sequence}/{Shot}/{Step}/work/{Shot}.{branch}.v{version}.{snapshot}.ma" # legacy style template object which only knows about the currently running operating system self.template_path_current_os_only = TemplatePath( self.definition, self.keys, self.project_root) project_root = os.path.join(self.tank_temp, "project_code") self._project_roots = {self.primary_root_name: {}} # Create the roots.yml like structure. Double down on the key names so it can be used in all scenarios # where we require the roots. for os_name in [ "windows_path", "linux_path", "mac_path", "win32", "linux2", "darwin", ]: self._project_roots[self.primary_root_name][os_name] = project_root self._primary_project_root = project_root # new style template object which supports all recognized platforms # get all OS roots for primary storage all_roots = self._project_roots[self.primary_root_name] self.template_path = TemplatePath(self.definition, self.keys, self.project_root, per_platform_roots=all_roots) self.project_root_template = TemplatePath("/", self.keys, self.project_root, per_platform_roots=all_roots) # make template with sequence key self.sequence = TemplatePath("/path/to/seq.{frame}.ext", self.keys, "", "frame")
def test_entity_field_query(self): """ Test template query to field linking to an entity. """ query_key = StringKey("shot_seq", shotgun_entity_type="Shot", shotgun_field_name="sg_sequence") self.keys["shot_seq"] = query_key # shot_extra cannot be gotten from path cache template_def = "/sequence/{Sequence}/{Shot}/{Step}/work/{shot_seq}.ext" template = TemplatePath(template_def, self.keys, self.project_root) result = self.ctx.as_template_fields(template) self.assertEquals("seq_name", result["shot_seq"])
def test_key_alias(self): """ Test that key's aliased name is used in template.keys dict. """ key = StringKey("alias_name") self.keys["not_alias_name"] = key definition = "something/{not_alias_name}" template = Template(definition, self.keys) template_key = template.keys["alias_name"] self.assertEqual(key, template_key) self.assertEqual("something/{alias_name}", template.definition)
def test_aliased_key(self): key = StringKey("aliased_name") self.keys["initial_name"] = key definition = "something/{Shot}/{initial_name}" template = Template(definition, self.keys) fields = {"aliased_name": "some value", "Shot": "shot value"} result = template.missing_keys(fields) self.assertEqual([], result) fields = {"initial_name": "some_value", "Shot": "shot value"} result = template.missing_keys(fields) self.assertEqual(["aliased_name"], result)
def test_template_query_none(self): """ Case that shogun returns None as value. """ # set field value to None self.shot["sg_sequence"] = None query_key = StringKey("shot_seq", shotgun_entity_type="Shot", shotgun_field_name="sg_sequence") self.keys["shot_seq"] = query_key template_def = "/sequence/{Sequence}/{Shot}/{Step}/work/{shot_seq}.ext" template = TemplatePath(template_def, self.keys, self.project_root) fields = self.ctx.as_template_fields(template) self.assertEquals(fields['shot_seq'], None)
def setUp(self): super(TestTemplate, self).setUp() # Make various types of keys(fields) self.keys = { "Sequence": StringKey("Sequence"), "Shot": StringKey("Shot", default="s1", choices=["s1", "s2", "shot_1"]), "Step": StringKey("Step"), "branch": StringKey("branch", filter_by="alphanumeric"), "name": StringKey("name"), "version": IntegerKey("version", format_spec="03"), "snapshot": IntegerKey("snapshot", format_spec="03"), "ext": StringKey("ext"), "seq_num": SequenceKey("seq_num"), "frame": SequenceKey("frame", format_spec="04"), "day_month_year": TimestampKey("day_month_year", format_spec="%d_%m_%Y") } # Make a template self.definition = "shots/{Sequence}/{Shot}/{Step}/work/{Shot}.{branch}.v{version}.{snapshot}.{day_month_year}.ma" self.template = Template(self.definition, self.keys)
def test_ambigous_alphanum_middle(self): """ Can't resolve if values are too ambiguous """ self.keys["favorites"] = StringKey("favorites") definition = "build/maya/{Asset}_{name_alpha}_{favorites}.ext" input_path = "build/maya/cat_man_doogle_fever.ext" template = TemplatePath(definition, self.keys, "") expected_msg = ( "Template %s: Ambiguous values found for key 'Asset' could be any of: 'cat', 'cat_man'" % template) self.check_error_message(TankError, expected_msg, template.get_fields, input_path)
def test_multi_ambiguous(self): """ Can't resolve if values are too ambiguous """ self.keys["favorites"] = StringKey("favorites") definition = "build/{Asset}_{name}_{favorites}/maya" input_path = "build/cat_man_doogle_do_dandy_dod/maya" template = TemplatePath(definition, self.keys, "") expected_msg = ( "Template %s: Ambiguous values found for key 'Asset' could be any of: " "'cat', 'cat_man', 'cat_man_doogle', 'cat_man_doogle_do'" % template) self.check_error_message(TankError, expected_msg, template.get_fields, input_path)
def test_optional_fields_not_in_template(self): """ Case that optional fields are specified, but not available in the template. """ field_name = "optional_field" self.keys[field_name] = StringKey(field_name) schema = {self.config_name:{"type":"template", "required_fields":[], "optional_fields": [field_name]}} # Template without the optional field template = tank.template.TemplatePath("{Shot}", self.keys, self.project_root) # tank instance with this template self.tk.templates={self.template_name:template} # If no error, then success validate_settings(self.app_name, self.tk, self.context, schema, self.config)
def test_default_values_detected(self): """ Case that field's value cannot be determined by the context, but field has a default value. """ # template with field with default value field_name = "field_1" self.keys[field_name] = StringKey(field_name, default="default") template = tank.template.TemplatePath("{%s}" % field_name, self.keys, self.project_root) # tank instance with this template self.tk.templates={self.template_name:template} # If no error, then success validate_settings(self.app_name, self.tk, self.context, self.metadata, self.config)