unitytk 0.0.7__tar.gz → 0.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {unitytk-0.0.7/unitytk.egg-info → unitytk-0.0.9}/PKG-INFO +4 -4
  2. {unitytk-0.0.7 → unitytk-0.0.9}/docs/README.md +1 -1
  3. {unitytk-0.0.7 → unitytk-0.0.9}/pyproject.toml +19 -2
  4. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_audio_events_integration.py +36 -118
  5. unitytk-0.0.9/test/test_base_test.py +617 -0
  6. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_bool_to_fade_curve.py +0 -1
  7. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_clip_name_contract_integration.py +21 -29
  8. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_controller_runtime_integration.py +12 -19
  9. unitytk-0.0.9/test/test_deploy_templates.py +412 -0
  10. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_emissive_groups_integration.py +23 -35
  11. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_fade_window_preservation.py +13 -21
  12. unitytk-0.0.7/test/test_c5m_visibility.py → unitytk-0.0.9/test/test_keyed_visibility.py +52 -23
  13. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_lightmap_metadata_integration.py +32 -97
  14. unitytk-0.0.7/test/test_c130_fcr_integration.py → unitytk-0.0.9/test/test_multi_section_audio_integration.py +85 -116
  15. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_render_opacity_controller.py +14 -23
  16. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_render_opacity_e2e.py +16 -22
  17. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_render_opacity_integration.py +14 -21
  18. unitytk-0.0.9/test/test_run_tests.py +271 -0
  19. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_shadow_plane_integration.py +21 -34
  20. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_shared_material_standalone.py +16 -15
  21. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_shot_metadata_integration.py +34 -100
  22. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_shots_audio_sidebyside_integration.py +35 -97
  23. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_standalone_enduser.py +40 -33
  24. unitytk-0.0.9/test/test_temp_projects.py +121 -0
  25. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/__init__.py +3 -2
  26. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/launcher.py +5 -4
  27. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/scene_builder.py +3 -2
  28. unitytk-0.0.9/unitytk/template_deployer.py +628 -0
  29. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/AudioEventController.cs +1 -1
  30. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/README.md +14 -4
  31. {unitytk-0.0.7 → unitytk-0.0.9/unitytk.egg-info}/PKG-INFO +4 -4
  32. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk.egg-info/SOURCES.txt +5 -2
  33. unitytk-0.0.7/test/test_deploy_templates.py +0 -198
  34. unitytk-0.0.7/unitytk/template_deployer.py +0 -237
  35. {unitytk-0.0.7 → unitytk-0.0.9}/LICENSE +0 -0
  36. {unitytk-0.0.7 → unitytk-0.0.9}/MANIFEST.in +0 -0
  37. {unitytk-0.0.7 → unitytk-0.0.9}/setup.cfg +0 -0
  38. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_asset_delivery.py +0 -0
  39. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_launcher.py +0 -0
  40. {unitytk-0.0.7 → unitytk-0.0.9}/test/test_scene_builder.py +0 -0
  41. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/asset_delivery.py +0 -0
  42. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/EmissiveGroupController.cs +0 -0
  43. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/EmissiveGroups.hlsl +0 -0
  44. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/EmissiveGroupsLit.shader +0 -0
  45. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/LightmapMetadataController.cs +0 -0
  46. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/M3trik.Unitytk.asmdef +0 -0
  47. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/RenderOpacityController.cs +0 -0
  48. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/ShadowPlaneController.cs +0 -0
  49. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/ShotMetadataController.cs +0 -0
  50. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/UnitytkSettings.cs +0 -0
  51. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk/templates/__init__.py +0 -0
  52. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk.egg-info/dependency_links.txt +0 -0
  53. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk.egg-info/requires.txt +0 -0
  54. {unitytk-0.0.7 → unitytk-0.0.9}/unitytk.egg-info/top_level.txt +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitytk
3
- Version: 0.0.7
3
+ Version: 0.0.9
4
4
  Summary: A modular Python toolkit for Unity interaction.
5
5
  Author-email: Ryan Simpson <m3trik@outlook.com>
6
6
  License: MIT
7
- Project-URL: Homepage, https://github.com/unitytk/unitytk
8
- Project-URL: Repository, https://github.com/unitytk/unitytk
7
+ Project-URL: Homepage, https://github.com/m3trik/unitytk
8
+ Project-URL: Repository, https://github.com/m3trik/unitytk
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
@@ -15,7 +15,7 @@ License-File: LICENSE
15
15
  Requires-Dist: pythontk>=0.7.71
16
16
  Dynamic: license-file
17
17
 
18
- [![Tests](https://img.shields.io/badge/Tests-124%20passed-brightgreen.svg)](../test/)
18
+ [![Tests](https://img.shields.io/badge/Tests-146%20passed-brightgreen.svg)](../test/)
19
19
 
20
20
  # unitytk
21
21
 
@@ -1,4 +1,4 @@
1
- [![Tests](https://img.shields.io/badge/Tests-124%20passed-brightgreen.svg)](../test/)
1
+ [![Tests](https://img.shields.io/badge/Tests-146%20passed-brightgreen.svg)](../test/)
2
2
 
3
3
  # unitytk
4
4
 
@@ -17,8 +17,8 @@ classifiers = [
17
17
  dependencies = ["pythontk>=0.7.71"]
18
18
 
19
19
  [project.urls]
20
- Homepage = "https://github.com/unitytk/unitytk"
21
- Repository = "https://github.com/unitytk/unitytk"
20
+ Homepage = "https://github.com/m3trik/unitytk"
21
+ Repository = "https://github.com/m3trik/unitytk"
22
22
 
23
23
  [tool.setuptools.dynamic]
24
24
  version = {attr = "unitytk.__version__"}
@@ -30,3 +30,20 @@ include = ["unitytk*"]
30
30
 
31
31
  [tool.setuptools.package-data]
32
32
  "*" = ["*.ui", "*.json", "*.txt", "*.md", "*.cs", "*.hlsl", "*.shader", "*.asmdef"]
33
+
34
+ [tool.pytest.ini_options]
35
+ testpaths = ["test"]
36
+ pythonpath = [".", "test"]
37
+ addopts = "--ignore=test/temp_tests"
38
+
39
+ [tool.ruff]
40
+ # House formatter/linter - rule set, exemptions and gate status: m3trik/docs/CODE_STANDARD.md section 1.
41
+ line-length = 88
42
+ target-version = "py39"
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E4", "E7", "E9", "F"]
46
+ ignore = ["E402"] # house pattern: try-guarded DCC imports precede the other imports
47
+
48
+ [tool.ruff.lint.per-file-ignores]
49
+ "**/templates/**" = ["F821"] # exec-templates carry __PLACEHOLDER__ tokens substituted at run time
@@ -28,24 +28,24 @@ import os
28
28
  import sys
29
29
  import json
30
30
  import shutil
31
- import tempfile
32
31
  import unittest
33
32
  import subprocess
34
33
  import logging
35
34
 
36
35
  logger = logging.getLogger(__name__)
37
36
 
38
- scripts_dir = r"O:\Cloud\Code\_scripts"
39
- if scripts_dir not in sys.path:
40
- sys.path.insert(0, scripts_dir)
37
+ import base_test # noqa: F401 — sys.path bootstrap for the sibling repos
38
+ from base_test import EnvSkip, MayaEnv, TempProjects, UnityEnv, UserPropsProbe
41
39
 
42
- from unitytk import UnityLauncher, UnityFinder, TemplateDeployer
43
-
44
- MAYAPY = r"C:\Program Files\Autodesk\Maya2025\bin\mayapy.exe"
40
+ scripts_dir = base_test.SCRIPTS_ROOT
45
41
 
42
+ from unitytk import UnityLauncher, UnityFinder, TemplateDeployer
46
43
 
47
- def _maya_available() -> bool:
48
- return os.path.exists(MAYAPY)
44
+ #: Resolved per machine -- UNITYTK_MAYAPY/MAYATK_MAYAPY, then the newest
45
+ #: standard install, then PATH (base_test.MayaEnv, same shape as mayatk's
46
+ #: runner). Pinning one install path made every gate below skip silently on
47
+ #: any other machine while the run still reported green.
48
+ MAYAPY = MayaEnv.mayapy()
49
49
 
50
50
 
51
51
  def _unity_available() -> bool:
@@ -60,9 +60,7 @@ MAYA_EXPORT_SCRIPT = r'''
60
60
  """Executed inside mayapy — keys audio tracks on data_internal and bakes the manifest."""
61
61
  import sys, os, json
62
62
 
63
- for pkg_dir in (r"O:\Cloud\Code\_scripts\mayatk", r"O:\Cloud\Code\_scripts\pythontk"):
64
- if pkg_dir not in sys.path:
65
- sys.path.insert(0, pkg_dir)
63
+ # mayatk/pythontk repo roots arrive via PYTHONPATH set by the parent test.
66
64
 
67
65
  import maya.standalone
68
66
  maya.standalone.initialize(name="python")
@@ -144,72 +142,11 @@ maya.standalone.uninitialize()
144
142
  # Unity-side C# scripts
145
143
  # ======================================================================
146
144
 
147
- # Captures every GameObject's user properties (keyed by GameObject name) so
148
- # we can verify that the manifest lands on data_export specifically. The
149
- # postprocessor fires once per GameObject with user-properties; we use a
150
- # static accumulator so each invocation merges into a single output file
151
- # instead of overwriting the previous one.
152
- UNITY_POSTPROCESSOR_CS = r"""
153
- using UnityEngine;
154
- using UnityEditor;
155
- using System.IO;
156
- using System.Collections.Generic;
157
-
158
- public class AudioEventTestPostprocessor : AssetPostprocessor
159
- {
160
- const string RESULTS_DIR = @"__RESULTS_DIR__";
161
-
162
- // Static so it survives across postprocessor invocations within the
163
- // same Unity editor session. Each GameObject with user-properties
164
- // triggers OnPostprocessGameObjectWithUserProperties once, and we
165
- // accumulate them all here before rewriting the JSON file.
166
- static readonly Dictionary<string, Dictionary<string, string>> _all =
167
- new Dictionary<string, Dictionary<string, string>>();
168
-
169
- void OnPreprocessModel()
170
- {
171
- var importer = assetImporter as ModelImporter;
172
- if (importer != null && !importer.importAnimatedCustomProperties)
173
- importer.importAnimatedCustomProperties = true;
174
- }
175
-
176
- void OnPostprocessGameObjectWithUserProperties(
177
- GameObject go, string[] propNames, object[] values)
178
- {
179
- var props = new Dictionary<string, string>();
180
- for (int i = 0; i < propNames.Length; i++)
181
- {
182
- string valStr = values[i] != null ? values[i].ToString() : "null";
183
- props[propNames[i]] = valStr;
184
- }
185
- _all[go.name] = props;
186
-
187
- string outPath = Path.Combine(RESULTS_DIR, "postprocessor_results.json");
188
- File.WriteAllText(outPath, DictToJson(_all));
189
- Debug.Log("[AudioEventTest] Captured " + propNames.Length
190
- + " user properties on " + go.name);
191
- }
192
-
193
- static string DictToJson(Dictionary<string, Dictionary<string, string>> all)
194
- {
195
- var outer = new List<string>();
196
- foreach (var go in all)
197
- {
198
- var inner = new List<string>();
199
- foreach (var kv in go.Value)
200
- inner.Add(" \"" + Escape(kv.Key) + "\": \"" + Escape(kv.Value) + "\"");
201
- outer.Add(" \"" + Escape(go.Key) + "\": {\n"
202
- + string.Join(",\n", inner) + "\n }");
203
- }
204
- return "{\n" + string.Join(",\n", outer) + "\n}";
205
- }
206
-
207
- static string Escape(string s)
208
- {
209
- return s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n");
210
- }
211
- }
212
- """
145
+ # The user-properties probe is the ONE shared template - see
146
+ # base_test.UserPropsProbe. Five suites each carried a near-identical copy,
147
+ # and the two halves of the carrier lookup had drifted (C# matched
148
+ # data_export by equality, Python by prefix), so one run could answer
149
+ # "is the carrier there?" two ways.
213
150
 
214
151
  UNITY_VERIFIER_CS = r"""
215
152
  using UnityEngine;
@@ -221,6 +158,7 @@ using System.Collections.Generic;
221
158
 
222
159
  public class AudioEventTestVerifier
223
160
  {
161
+ __CARRIER_MATCH__
224
162
  public static void Verify()
225
163
  {
226
164
  string resultsFile = @"__RESULTS_PATH__";
@@ -263,7 +201,7 @@ public class AudioEventTestVerifier
263
201
  foreach (var t in prefab.GetComponentsInChildren<Transform>(true))
264
202
  {
265
203
  goNames.Add(t.name);
266
- if (dataExport == null && t.name == "data_export")
204
+ if (dataExport == null && IsCarrier(t.name))
267
205
  dataExport = t;
268
206
  }
269
207
  }
@@ -462,11 +400,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
462
400
 
463
401
  @classmethod
464
402
  def setUpClass(cls):
465
- # In-repo scratch (see CLAUDE.md), not the system temp dir —
466
- # these preserve a full Unity project on purpose.
467
- _temp_base = os.path.join(os.path.dirname(__file__), "temp_tests")
468
- os.makedirs(_temp_base, exist_ok=True)
469
- cls.temp_dir = tempfile.mkdtemp(prefix="audio_events_integ_", dir=_temp_base)
403
+ cls.temp_dir = TempProjects.allocate("audio_events_integ")
470
404
  cls.fbx_path = None
471
405
  cls.maya_results = None
472
406
  cls.unity_results = None
@@ -481,7 +415,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
481
415
  # Step 1: Maya export via AudioClips.prepare_for_export
482
416
  # ------------------------------------------------------------------
483
417
 
484
- @unittest.skipUnless(_maya_available(), "Maya 2025 not installed")
418
+ @MayaEnv.skip_unless_available()
485
419
  def test_01_maya_export(self):
486
420
  """Author tracks via AudioUtils, bake manifest via AudioClips, export FBX."""
487
421
  script_path = os.path.join(self.temp_dir, "maya_export.py")
@@ -540,8 +474,8 @@ class TestAudioEventsIntegration(unittest.TestCase):
540
474
  # Step 2: Unity import and readback
541
475
  # ------------------------------------------------------------------
542
476
 
543
- @unittest.skipUnless(_maya_available(), "Maya 2025 not installed")
544
- @unittest.skipUnless(_unity_available(), "No Unity editor found")
477
+ @MayaEnv.skip_unless_available()
478
+ @EnvSkip.unless(_unity_available(), "no Unity editor found")
545
479
  def test_02_unity_import(self):
546
480
  """Import FBX with real controllers deployed; verify manifest + auto-attach."""
547
481
  fbx_path = self.__class__.fbx_path
@@ -557,11 +491,7 @@ class TestAudioEventsIntegration(unittest.TestCase):
557
491
 
558
492
  logger.info(f"Creating Unity project: {project_path}")
559
493
  if not launcher.create_project(project_path, batch_mode=True):
560
- self.skipTest(
561
- "Unity batch project creation failed — typically no batch-mode "
562
- "license on this machine (see Unity Editor.log). The Maya export "
563
- "half (test_01) still validates the FBX contents."
564
- )
494
+ self.skipTest(UnityEnv.project_creation_reason())
565
495
 
566
496
  assets_path = os.path.join(project_path, "Assets")
567
497
  # create_project reports success on directory existence but may not have
@@ -578,22 +508,13 @@ class TestAudioEventsIntegration(unittest.TestCase):
578
508
  editor_path = os.path.join(assets_path, "Editor")
579
509
  os.makedirs(editor_path, exist_ok=True)
580
510
 
581
- results_dir_escaped = self.temp_dir.replace("\\", "\\\\")
582
- postproc_cs = UNITY_POSTPROCESSOR_CS.replace(
583
- "__RESULTS_DIR__", results_dir_escaped
584
- )
585
- with open(
586
- os.path.join(editor_path, "AudioEventTestPostprocessor.cs"),
587
- "w",
588
- encoding="utf-8",
589
- ) as f:
590
- f.write(postproc_cs)
511
+ UserPropsProbe.deploy(editor_path, self.temp_dir)
591
512
 
592
513
  results_path = os.path.join(self.temp_dir, "unity_results.json")
593
514
  results_path_escaped = results_path.replace("\\", "\\\\")
594
515
  verifier_cs = UNITY_VERIFIER_CS.replace(
595
516
  "__RESULTS_PATH__", results_path_escaped
596
- )
517
+ ).replace("__CARRIER_MATCH__", UserPropsProbe.carrier_match_cs())
597
518
  with open(
598
519
  os.path.join(editor_path, "AudioEventTestVerifier.cs"),
599
520
  "w",
@@ -644,10 +565,11 @@ class TestAudioEventsIntegration(unittest.TestCase):
644
565
  if not os.path.exists(results_path):
645
566
  # A batch-licensing failure is environmental (not a code bug) — skip.
646
567
  if UnityLauncher.is_license_failure(returncode, full_log):
647
- self.skipTest(
648
- "Unity exited without a valid batch license "
649
- f"(rc={returncode}) — see {log_path}"
568
+ logger.info(
569
+ "Unity batch license failure (rc=%s) see %s",
570
+ returncode, log_path,
650
571
  )
572
+ self.skipTest(UnityEnv.license_reason())
651
573
  self.fail(
652
574
  f"unity_results.json not created (Unity rc={returncode}) — "
653
575
  f"check {log_path}"
@@ -655,7 +577,8 @@ class TestAudioEventsIntegration(unittest.TestCase):
655
577
  with open(results_path) as f:
656
578
  self.__class__.unity_results = json.load(f)
657
579
 
658
- postproc_path = os.path.join(self.temp_dir, "postprocessor_results.json")
580
+ postproc_path = os.path.join(
581
+ self.temp_dir, UserPropsProbe.RESULTS_FILENAME)
659
582
  if os.path.exists(postproc_path):
660
583
  with open(postproc_path) as f:
661
584
  self.__class__.postproc = json.load(f)
@@ -668,8 +591,8 @@ class TestAudioEventsIntegration(unittest.TestCase):
668
591
  # Step 3: Verify manifest + auto-attach + AnimationEvents
669
592
  # ------------------------------------------------------------------
670
593
 
671
- @unittest.skipUnless(_maya_available(), "Maya 2025 not installed")
672
- @unittest.skipUnless(_unity_available(), "No Unity editor found")
594
+ @MayaEnv.skip_unless_available()
595
+ @EnvSkip.unless(_unity_available(), "no Unity editor found")
673
596
  def test_03_analyze(self):
674
597
  """End-to-end: manifest, prefab shape, controller attach, AnimationEvents."""
675
598
  unity = self.__class__.unity_results
@@ -684,17 +607,12 @@ class TestAudioEventsIntegration(unittest.TestCase):
684
607
  )
685
608
 
686
609
  postproc = self.__class__.postproc or {}
687
- carrier_entry = None
688
- for name, props in postproc.items():
689
- if name.lower().startswith("data_export"):
690
- carrier_entry = (name, props)
691
- break
610
+ name, props = UserPropsProbe.find_carrier(postproc)
692
611
  self.assertIsNotNone(
693
- carrier_entry,
694
- f"No GameObject with name starting 'data_export' found in "
695
- f"postprocessor results: {list(postproc)}",
612
+ name,
613
+ f"No GameObject named {UserPropsProbe.CARRIER!r} (prefix match) "
614
+ f"in postprocessor results: {list(postproc)}",
696
615
  )
697
- name, props = carrier_entry
698
616
  self.assertIn(
699
617
  "audio_manifest", props,
700
618
  f"audio_manifest user-property missing on {name}; saw: {list(props)}",