<!-- Generated by SpecStory, Markdown v2.1.0 -->

# 2026-01-28 19:46:07Z

<!-- cursoride Session dc1167a0-5b51-4814-bb00-9c37354a72fa (2026-01-28 19:46:07Z) -->

_**User**_

I'm modifying the addon. context:@c:\Users\Nathan\Desktop\flamenco\.specstory\history\2026-01-09_20-34Z-flamenco-project-file-path-consistency.md 

---

_**Agent (default)**_

Reviewing the context file to understand the addon modifications.


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: C:\Users\Nathan\Desktop\flamenco\.specstory\history\2026-01-09_20-34Z-flamenco-project-file-path-consistency.md</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="list_dir_v2">
<details>
<summary>Tool use: **list_dir_v2**</summary>


Parameters:

```json
{
  "targetDirectory": "e:\\flamenco\\addon"
}
```
Additional data:

```json
{
  "extensionCounts": {
    ".py": 89
  },
  "isPruned": true,
  "rootPath": "e:\\flamenco\\addon",
  "topEntries": [
    {
      "isDir": true,
      "numFiles": 89,
      "path": "e:\\flamenco\\addon\\flamenco"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/.openapi-generator-ignore"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/mypy.ini"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/openapi-generator-cli.jar"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/poetry.lock"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/pyproject.toml"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/README.md"
    }
  ],
  "totalDirs": 9,
  "totalFiles": 138
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="list_dir_v2"><details>
<summary>Tool use: **list_dir_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Reading the beginning of the context file to understand the task:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: C:\Users\Nathan\Desktop\flamenco\.specstory\history\2026-01-09_20-34Z-flamenco-project-file-path-consistency.md</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Searching the context file and addon codebase for relevant sections:


_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
<details>
<summary>Tool use: **ripgrep_raw_search**</summary>


Parameters:

```json
{
  "caseInsensitive": true,
  "path": "C:\\Users\\Nathan\\Desktop\\flamenco\\.specstory\\history\\2026-01-09_20-34Z-flamenco-project-file-path-consistency.md",
  "pattern": "abspath|absolute path|BAT|packing"
}
```
Additional data:

```json
{
  "isPruned": true,
  "outputMode": "content",
  "path": "C:\\Users\\Nathan\\Desktop\\flamenco\\.specstory\\history\\2026-01-09_20-34Z-flamenco-project-file-path-consistency.md",
  "pattern": "abspath|absolute path|BAT|packing",
  "topFiles": [
    {
      "matchCount": 50,
      "uri": "C:\\Users\\Nathan\\Desktop\\flamenco\\.specstory\\history\\2026-01-09_20-34Z-flamenco-project-file-path-consistency.md"
    }
  ],
  "totalFiles": 1,
  "totalMatches": 50
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search"><details>
<summary>Tool use: **ripgrep_raw_search**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full">
<details>
<summary>Tool use: **semantic_search_full**</summary>


Parameters:

```json
{
  "explanation": "",
  "includePattern": "addon",
  "query": "How does the Flamenco addon send project files and handle file paths when submitting jobs?",
  "topK": 10
}
```
Result:

```json
{
  "codeResults": [
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:\n\n\n        return True\n\n    def _bat_pack_filesystem(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e PurePosixPath:\n        \"\"\"Use BAT to store the pack on the filesystem.\n\n        :return: the path of the blend file, for use in the job definition.\n        \"\"\"\n        from .bat import interface as bat_interface\n\n        # Get project path from addon preferences.\n        prefs = preferences.get(context)\n        project_path: Path = prefs.project_root()\n        project_path = bpathlib.make_absolute(Path(bpy.path.abspath(str(project_path))))\n\n        if not project_path.exists():\n            self.report({\"ERROR\"}, \"Project path %s does not exist\" % project_path)\n            raise FileNotFoundError()\n\n        # Determine where the blend file will be stored.\n        manager = self._manager_info(context)\n        if not manager:\n            raise FileNotFoundError(\"Manager info not known\")\n        unique_dir = \"%s-%s\" % (\n            datetime.datetime.now().isoformat(\"-\").replace(\":\", \"\"),\n            self.job_name,\n        )\n        pack_target_dir = Path(manager.shared_storage.location) / unique_dir\n\n        # TODO: this should take the blendfile location relative to the project path into account.\n        pack_target_file = pack_target_dir / blendfile.name\n        self.log.info(\"Will store blend file at %s\", pack_target_file)\n\n        self.packthread = bat_interface.copy(\n            base_blendfile=blendfile,\n            project=project_path,\n            target=str(pack_target_dir),\n            exclusion_filter=\"\",  # TODO: get from GUI.\n            relative_only=True,  # TODO: get from GUI.\n        )\n\n        return PurePosixPath(pack_target_file.as_posix())\n\n    def _shaman_checkout_path(self) -\u003e PurePosixPath:\n        \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 366,
            "text": "def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 404
          },
          {
            "lineNumber": 405
          },
          {
            "lineNumber": 406,
            "text": "        return True"
          },
          {
            "lineNumber": 407
          },
          {
            "lineNumber": 408,
            "text": "    def _bat_pack_filesystem("
          },
          {
            "lineNumber": 409,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 410,
            "text": "    ) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 411,
            "text": "        \"\"\"Use BAT to store the pack on the filesystem."
          },
          {
            "lineNumber": 412
          },
          {
            "lineNumber": 413,
            "text": "        :return: the path of the blend file, for use in the job definition."
          },
          {
            "lineNumber": 414,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 415,
            "text": "        from .bat import interface as bat_interface"
          },
          {
            "lineNumber": 416
          },
          {
            "lineNumber": 417,
            "text": "        # Get project path from addon preferences."
          },
          {
            "lineNumber": 418,
            "text": "        prefs = preferences.get(context)"
          },
          {
            "lineNumber": 419,
            "text": "        project_path: Path = prefs.project_root()"
          },
          {
            "lineNumber": 420,
            "text": "        project_path = bpathlib.make_absolute(Path(bpy.path.abspath(str(project_path))))"
          },
          {
            "lineNumber": 421
          },
          {
            "lineNumber": 422,
            "text": "        if not project_path.exists():"
          },
          {
            "lineNumber": 423,
            "text": "            self.report({\"ERROR\"}, \"Project path %s does not exist\" % project_path)"
          },
          {
            "lineNumber": 424,
            "text": "            raise FileNotFoundError()"
          },
          {
            "lineNumber": 425
          },
          {
            "lineNumber": 426,
            "text": "        # Determine where the blend file will be stored."
          },
          {
            "lineNumber": 427,
            "text": "        manager = self._manager_info(context)"
          },
          {
            "lineNumber": 428,
            "text": "        if not manager:"
          },
          {
            "lineNumber": 429,
            "text": "            raise FileNotFoundError(\"Manager info not known\")"
          },
          {
            "lineNumber": 430,
            "text": "        unique_dir = \"%s-%s\" % ("
          },
          {
            "lineNumber": 431,
            "text": "            datetime.datetime.now().isoformat(\"-\").replace(\":\", \"\"),"
          },
          {
            "lineNumber": 432,
            "text": "            self.job_name,"
          },
          {
            "lineNumber": 433,
            "text": "        )"
          },
          {
            "lineNumber": 434,
            "text": "        pack_target_dir = Path(manager.shared_storage.location) / unique_dir"
          },
          {
            "lineNumber": 435
          },
          {
            "lineNumber": 436,
            "text": "        # TODO: this should take the blendfile location relative to the project path into account."
          },
          {
            "lineNumber": 437,
            "text": "        pack_target_file = pack_target_dir / blendfile.name"
          },
          {
            "lineNumber": 438,
            "text": "        self.log.info(\"Will store blend file at %s\", pack_target_file)"
          },
          {
            "lineNumber": 439
          },
          {
            "lineNumber": 440,
            "text": "        self.packthread = bat_interface.copy("
          },
          {
            "lineNumber": 441,
            "text": "            base_blendfile=blendfile,"
          },
          {
            "lineNumber": 442,
            "text": "            project=project_path,"
          },
          {
            "lineNumber": 443,
            "text": "            target=str(pack_target_dir),"
          },
          {
            "lineNumber": 444,
            "text": "            exclusion_filter=\"\",  # TODO: get from GUI."
          },
          {
            "lineNumber": 445,
            "text": "            relative_only=True,  # TODO: get from GUI."
          },
          {
            "lineNumber": 446,
            "text": "        )"
          },
          {
            "lineNumber": 447
          },
          {
            "lineNumber": 448,
            "text": "        return PurePosixPath(pack_target_file.as_posix())"
          },
          {
            "lineNumber": 449
          },
          {
            "lineNumber": 450,
            "text": "    def _shaman_checkout_path(self) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 451,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.548140287399292
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n\n\n    def _shaman_checkout_path(self) -\u003e PurePosixPath:\n        \"\"\"Construct the Shaman checkout path, aka Shaman Checkout ID.\n\n        Note that this may not be the actually used checkout ID, as that will be\n        made unique to this job by Flamenco Manager. That will be stored in\n        self.actual_shaman_checkout_path after the Shaman checkout is actually\n        done.\n        \"\"\"\n        assert self.job is not None\n\n        # TODO: get project name from preferences/GUI and insert that here too.\n        return PurePosixPath(f\"{self.job.name}\")\n\n    def _bat_pack_shaman(self, context: bpy.types.Context, blendfile: Path) -\u003e None:\n        \"\"\"Use the Manager's Shaman API to submit the BAT pack.\n\n        :return: the filesystem path of the blend file, for in the render job definition.\n        \"\"\"\n        from .bat import (\n            interface as bat_interface,\n            shaman as bat_shaman,\n        )\n\n        assert self.job is not None\n        self.log.info(\"Sending BAT pack to Shaman\")\n\n        prefs = preferences.get(context)\n        project_path: Path = prefs.project_root()\n\n        self.packthread = bat_interface.copy(\n            base_blendfile=blendfile,\n            project=project_path,\n            target=\"/\",  # Target directory irrelevant for Shaman transfers.\n            exclusion_filter=\"\",  # TODO: get from GUI.\n            relative_only=True,  # TODO: get from GUI.\n            packer_class=bat_shaman.Packer,\n            packer_kwargs=dict(\n                api_client=self.get_api_client(context),\n                checkout_path=self._shaman_checkout_path(),\n            ),\n        )\n\n        # We cannot assume the blendfile location is known until the Shaman\n        # checkout has actually been created.\n\n    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 448
          },
          {
            "lineNumber": 449
          },
          {
            "lineNumber": 450,
            "text": "    def _shaman_checkout_path(self) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 451,
            "text": "        \"\"\"Construct the Shaman checkout path, aka Shaman Checkout ID."
          },
          {
            "lineNumber": 452
          },
          {
            "lineNumber": 453,
            "text": "        Note that this may not be the actually used checkout ID, as that will be"
          },
          {
            "lineNumber": 454,
            "text": "        made unique to this job by Flamenco Manager. That will be stored in"
          },
          {
            "lineNumber": 455,
            "text": "        self.actual_shaman_checkout_path after the Shaman checkout is actually"
          },
          {
            "lineNumber": 456,
            "text": "        done."
          },
          {
            "lineNumber": 457,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 458,
            "text": "        assert self.job is not None"
          },
          {
            "lineNumber": 459
          },
          {
            "lineNumber": 460,
            "text": "        # TODO: get project name from preferences/GUI and insert that here too."
          },
          {
            "lineNumber": 461,
            "text": "        return PurePosixPath(f\"{self.job.name}\")"
          },
          {
            "lineNumber": 462
          },
          {
            "lineNumber": 463,
            "text": "    def _bat_pack_shaman(self, context: bpy.types.Context, blendfile: Path) -\u003e None:"
          },
          {
            "lineNumber": 464,
            "text": "        \"\"\"Use the Manager's Shaman API to submit the BAT pack."
          },
          {
            "lineNumber": 465
          },
          {
            "lineNumber": 466,
            "text": "        :return: the filesystem path of the blend file, for in the render job definition."
          },
          {
            "lineNumber": 467,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 468,
            "text": "        from .bat import ("
          },
          {
            "lineNumber": 469,
            "text": "            interface as bat_interface,"
          },
          {
            "lineNumber": 470,
            "text": "            shaman as bat_shaman,"
          },
          {
            "lineNumber": 471,
            "text": "        )"
          },
          {
            "lineNumber": 472
          },
          {
            "lineNumber": 473,
            "text": "        assert self.job is not None"
          },
          {
            "lineNumber": 474,
            "text": "        self.log.info(\"Sending BAT pack to Shaman\")"
          },
          {
            "lineNumber": 475
          },
          {
            "lineNumber": 476,
            "text": "        prefs = preferences.get(context)"
          },
          {
            "lineNumber": 477,
            "text": "        project_path: Path = prefs.project_root()"
          },
          {
            "lineNumber": 478
          },
          {
            "lineNumber": 479,
            "text": "        self.packthread = bat_interface.copy("
          },
          {
            "lineNumber": 480,
            "text": "            base_blendfile=blendfile,"
          },
          {
            "lineNumber": 481,
            "text": "            project=project_path,"
          },
          {
            "lineNumber": 482,
            "text": "            target=\"/\",  # Target directory irrelevant for Shaman transfers."
          },
          {
            "lineNumber": 483,
            "text": "            exclusion_filter=\"\",  # TODO: get from GUI."
          },
          {
            "lineNumber": 484,
            "text": "            relative_only=True,  # TODO: get from GUI."
          },
          {
            "lineNumber": 485,
            "text": "            packer_class=bat_shaman.Packer,"
          },
          {
            "lineNumber": 486,
            "text": "            packer_kwargs=dict("
          },
          {
            "lineNumber": 487,
            "text": "                api_client=self.get_api_client(context),"
          },
          {
            "lineNumber": 488,
            "text": "                checkout_path=self._shaman_checkout_path(),"
          },
          {
            "lineNumber": 489,
            "text": "            ),"
          },
          {
            "lineNumber": 490,
            "text": "        )"
          },
          {
            "lineNumber": 491
          },
          {
            "lineNumber": 492,
            "text": "        # We cannot assume the blendfile location is known until the Shaman"
          },
          {
            "lineNumber": 493,
            "text": "        # checkout has actually been created."
          },
          {
            "lineNumber": 494
          },
          {
            "lineNumber": 495,
            "text": "    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:"
          },
          {
            "lineNumber": 496,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.507941722869873
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n\n\n    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:\n        from .bat import interface as bat_interface\n\n        if isinstance(msg, bat_interface.MsgDone):\n            if self.blendfile_on_farm is None:\n                # Adjust the blendfile to match the Shaman checkout path. Shaman\n                # may have checked out at a different location than we\n                # requested.\n                #\n                # Manager automatically creates a variable \"jobs\" that will\n                # resolve to the job storage directory.\n                self.blendfile_on_farm = PurePosixPath(\"{jobs}\") / msg.output_path\n\n            self.actual_shaman_checkout_path = msg.actual_checkout_path\n            self._submit_job(context)\n            return self._quit(context)\n\n        if isinstance(msg, bat_interface.MsgException):\n            self.log.error(\"Error performing BAT pack: %s\", msg.ex)\n            self.report({\"ERROR\"}, \"Error performing BAT pack: %s\" % msg.ex)\n\n            # This was an exception caught at the top level of the thread, so\n            # the packing thread itself has stopped.\n            return self._quit(context)\n\n        if isinstance(msg, bat_interface.MsgSetWMAttribute):\n            wm = context.window_manager\n            setattr(wm, msg.attribute_name, msg.value)\n\n        return {\"RUNNING_MODAL\"}\n\n    def _use_blendfile_directly(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e None:\n        # The temporary '.flamenco.blend' file should not be deleted, as it\n        # will be used directly by the render job.\n        self.temp_blendfile = None\n\n        # The blend file is contained in the job storage path, no need to\n        # copy anything.\n        self.blendfile_on_farm = bpathlib.make_absolute(blendfile)\n\n        # No Shaman is involved when using the file directly.\n        self.actual_shaman_checkout_path = None\n\n        self._submit_job(context)\n\n    def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 493
          },
          {
            "lineNumber": 494
          },
          {
            "lineNumber": 495,
            "text": "    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:"
          },
          {
            "lineNumber": 496,
            "text": "        from .bat import interface as bat_interface"
          },
          {
            "lineNumber": 497
          },
          {
            "lineNumber": 498,
            "text": "        if isinstance(msg, bat_interface.MsgDone):"
          },
          {
            "lineNumber": 499,
            "text": "            if self.blendfile_on_farm is None:"
          },
          {
            "lineNumber": 500,
            "text": "                # Adjust the blendfile to match the Shaman checkout path. Shaman"
          },
          {
            "lineNumber": 501,
            "text": "                # may have checked out at a different location than we"
          },
          {
            "lineNumber": 502,
            "text": "                # requested."
          },
          {
            "lineNumber": 503,
            "text": "                #"
          },
          {
            "lineNumber": 504,
            "text": "                # Manager automatically creates a variable \"jobs\" that will"
          },
          {
            "lineNumber": 505,
            "text": "                # resolve to the job storage directory."
          },
          {
            "lineNumber": 506,
            "text": "                self.blendfile_on_farm = PurePosixPath(\"{jobs}\") / msg.output_path"
          },
          {
            "lineNumber": 507
          },
          {
            "lineNumber": 508,
            "text": "            self.actual_shaman_checkout_path = msg.actual_checkout_path"
          },
          {
            "lineNumber": 509,
            "text": "            self._submit_job(context)"
          },
          {
            "lineNumber": 510,
            "text": "            return self._quit(context)"
          },
          {
            "lineNumber": 511
          },
          {
            "lineNumber": 512,
            "text": "        if isinstance(msg, bat_interface.MsgException):"
          },
          {
            "lineNumber": 513,
            "text": "            self.log.error(\"Error performing BAT pack: %s\", msg.ex)"
          },
          {
            "lineNumber": 514,
            "text": "            self.report({\"ERROR\"}, \"Error performing BAT pack: %s\" % msg.ex)"
          },
          {
            "lineNumber": 515
          },
          {
            "lineNumber": 516,
            "text": "            # This was an exception caught at the top level of the thread, so"
          },
          {
            "lineNumber": 517,
            "text": "            # the packing thread itself has stopped."
          },
          {
            "lineNumber": 518,
            "text": "            return self._quit(context)"
          },
          {
            "lineNumber": 519
          },
          {
            "lineNumber": 520,
            "text": "        if isinstance(msg, bat_interface.MsgSetWMAttribute):"
          },
          {
            "lineNumber": 521,
            "text": "            wm = context.window_manager"
          },
          {
            "lineNumber": 522,
            "text": "            setattr(wm, msg.attribute_name, msg.value)"
          },
          {
            "lineNumber": 523
          },
          {
            "lineNumber": 524,
            "text": "        return {\"RUNNING_MODAL\"}"
          },
          {
            "lineNumber": 525
          },
          {
            "lineNumber": 526,
            "text": "    def _use_blendfile_directly("
          },
          {
            "lineNumber": 527,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 528,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 529,
            "text": "        # The temporary '.flamenco.blend' file should not be deleted, as it"
          },
          {
            "lineNumber": 530,
            "text": "        # will be used directly by the render job."
          },
          {
            "lineNumber": 531,
            "text": "        self.temp_blendfile = None"
          },
          {
            "lineNumber": 532
          },
          {
            "lineNumber": 533,
            "text": "        # The blend file is contained in the job storage path, no need to"
          },
          {
            "lineNumber": 534,
            "text": "        # copy anything."
          },
          {
            "lineNumber": 535,
            "text": "        self.blendfile_on_farm = bpathlib.make_absolute(blendfile)"
          },
          {
            "lineNumber": 536
          },
          {
            "lineNumber": 537,
            "text": "        # No Shaman is involved when using the file directly."
          },
          {
            "lineNumber": 538,
            "text": "        self.actual_shaman_checkout_path = None"
          },
          {
            "lineNumber": 539
          },
          {
            "lineNumber": 540,
            "text": "        self._submit_job(context)"
          },
          {
            "lineNumber": 541
          },
          {
            "lineNumber": 542,
            "text": "    def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 543,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.4325915575027466
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _use_blendfile_directly(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e None:\n        # The temporary '.flamenco.blend' file should not be deleted, as it\n        # will be used directly by the render job.\n\n\n        self._submit_job(context)\n\n    def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:\n        \"\"\"Prepare self.job for sending to Flamenco.\"\"\"\n\n        self.job = job_submission.job_for_scene(context.scene)\n        if self.job is None:\n            self.report({\"ERROR\"}, \"Unable to create job\")\n            return False\n\n        propgroup = getattr(context.scene, \"flamenco_job_settings\", None)\n        assert isinstance(propgroup, JobTypePropertyGroup), \"did not expect %s\" % (\n            type(propgroup)\n        )\n        propgroup.eval_hidden_settings_of_job(context, self.job)\n\n        job_submission.set_blend_file(\n            propgroup.job_type,\n            self.job,\n            # self.blendfile_on_farm is None when we're just checking the job.\n            self.blendfile_on_farm or \"dummy-for-job-check.blend\",\n        )\n\n        if self.actual_shaman_checkout_path:\n            job_submission.set_shaman_checkout_id(\n                self.job, self.actual_shaman_checkout_path\n            )\n\n        return True\n\n    def _submit_job(self, context: bpy.types.Context) -\u003e None:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 526,
            "text": "def _use_blendfile_directly("
          },
          {
            "lineNumber": 527,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 528,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 529,
            "text": "        # The temporary '.flamenco.blend' file should not be deleted, as it"
          },
          {
            "lineNumber": 530,
            "text": "        # will be used directly by the render job."
          },
          {
            "lineNumber": 538
          },
          {
            "lineNumber": 539
          },
          {
            "lineNumber": 540,
            "text": "        self._submit_job(context)"
          },
          {
            "lineNumber": 541
          },
          {
            "lineNumber": 542,
            "text": "    def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 543,
            "text": "        \"\"\"Prepare self.job for sending to Flamenco.\"\"\""
          },
          {
            "lineNumber": 544
          },
          {
            "lineNumber": 545,
            "text": "        self.job = job_submission.job_for_scene(context.scene)"
          },
          {
            "lineNumber": 546,
            "text": "        if self.job is None:"
          },
          {
            "lineNumber": 547,
            "text": "            self.report({\"ERROR\"}, \"Unable to create job\")"
          },
          {
            "lineNumber": 548,
            "text": "            return False"
          },
          {
            "lineNumber": 549
          },
          {
            "lineNumber": 550,
            "text": "        propgroup = getattr(context.scene, \"flamenco_job_settings\", None)"
          },
          {
            "lineNumber": 551,
            "text": "        assert isinstance(propgroup, JobTypePropertyGroup), \"did not expect %s\" % ("
          },
          {
            "lineNumber": 552,
            "text": "            type(propgroup)"
          },
          {
            "lineNumber": 553,
            "text": "        )"
          },
          {
            "lineNumber": 554,
            "text": "        propgroup.eval_hidden_settings_of_job(context, self.job)"
          },
          {
            "lineNumber": 555
          },
          {
            "lineNumber": 556,
            "text": "        job_submission.set_blend_file("
          },
          {
            "lineNumber": 557,
            "text": "            propgroup.job_type,"
          },
          {
            "lineNumber": 558,
            "text": "            self.job,"
          },
          {
            "lineNumber": 559,
            "text": "            # self.blendfile_on_farm is None when we're just checking the job."
          },
          {
            "lineNumber": 560,
            "text": "            self.blendfile_on_farm or \"dummy-for-job-check.blend\","
          },
          {
            "lineNumber": 561,
            "text": "        )"
          },
          {
            "lineNumber": 562
          },
          {
            "lineNumber": 563,
            "text": "        if self.actual_shaman_checkout_path:"
          },
          {
            "lineNumber": 564,
            "text": "            job_submission.set_shaman_checkout_id("
          },
          {
            "lineNumber": 565,
            "text": "                self.job, self.actual_shaman_checkout_path"
          },
          {
            "lineNumber": 566,
            "text": "            )"
          },
          {
            "lineNumber": 567
          },
          {
            "lineNumber": 568,
            "text": "        return True"
          },
          {
            "lineNumber": 569
          },
          {
            "lineNumber": 570,
            "text": "    def _submit_job(self, context: bpy.types.Context) -\u003e None:"
          },
          {
            "lineNumber": 571,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.42707836627960205
    },
    {
      "codeBlock": {
        "contents": "def set_blend_file(\n    job_type: _AvailableJobType,\n    job: _SubmittedJob,\n    blendfile: Union[str, Path, PurePosixPath],\n) -\u003e None:\n    \"\"\"Update the job's 'blendfile' setting, if available.\n\n    If a job has a 'blendfile' setting, type 'str', it'll be set to the path/URL\n    of the BAT-packed blend file.\n    \"\"\"\n    from .manager.models import AvailableJobSetting, AvailableJobSettingType\n\n    expected_type = AvailableJobSettingType(\"string\")\n    for setting in job_type.settings:\n        if setting.key == BLENDFILE_SETTING_KEY and setting.type == expected_type:\n            break\n    else:\n        # Not having this setting is fine.\n        return\n\n    assert isinstance(setting, AvailableJobSetting)\n    job.settings[BLENDFILE_SETTING_KEY] = str(blendfile)\n\n\ndef set_shaman_checkout_id(job: _SubmittedJob, checkout_id: PurePosixPath) -\u003e None:\n    ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 72,
            "text": "def set_blend_file("
          },
          {
            "isSignature": true,
            "lineNumber": 73,
            "text": "    job_type: _AvailableJobType,"
          },
          {
            "isSignature": true,
            "lineNumber": 74,
            "text": "    job: _SubmittedJob,"
          },
          {
            "isSignature": true,
            "lineNumber": 75,
            "text": "    blendfile: Union[str, Path, PurePosixPath],"
          },
          {
            "isSignature": true,
            "lineNumber": 76,
            "text": ") -\u003e None:"
          },
          {
            "lineNumber": 77,
            "text": "    \"\"\"Update the job's 'blendfile' setting, if available."
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "    If a job has a 'blendfile' setting, type 'str', it'll be set to the path/URL"
          },
          {
            "lineNumber": 80,
            "text": "    of the BAT-packed blend file."
          },
          {
            "lineNumber": 81,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 82,
            "text": "    from .manager.models import AvailableJobSetting, AvailableJobSettingType"
          },
          {
            "lineNumber": 83
          },
          {
            "lineNumber": 84,
            "text": "    expected_type = AvailableJobSettingType(\"string\")"
          },
          {
            "lineNumber": 85,
            "text": "    for setting in job_type.settings:"
          },
          {
            "lineNumber": 86,
            "text": "        if setting.key == BLENDFILE_SETTING_KEY and setting.type == expected_type:"
          },
          {
            "lineNumber": 87,
            "text": "            break"
          },
          {
            "lineNumber": 88,
            "text": "    else:"
          },
          {
            "lineNumber": 89,
            "text": "        # Not having this setting is fine."
          },
          {
            "lineNumber": 90,
            "text": "        return"
          },
          {
            "lineNumber": 91
          },
          {
            "lineNumber": 92,
            "text": "    assert isinstance(setting, AvailableJobSetting)"
          },
          {
            "isSignature": true,
            "lineNumber": 93,
            "text": "    job.settings[BLENDFILE_SETTING_KEY] = str(blendfile)"
          },
          {
            "lineNumber": 94
          },
          {
            "lineNumber": 95
          },
          {
            "isSignature": true,
            "lineNumber": 96,
            "text": "def set_shaman_checkout_id(job: _SubmittedJob, checkout_id: PurePosixPath) -\u003e None:"
          },
          {
            "lineNumber": 97,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 101
          },
          "startPosition": {
            "column": 14,
            "line": 68
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.41990891098976135
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\nfrom pathlib import Path, PurePosixPath\nfrom typing import TYPE_CHECKING, Optional, Union\nimport platform\nimport logging\n\nimport bpy\n\nfrom .job_types_propgroup import JobTypePropertyGroup\nfrom .bat.submodules import bpathlib\nfrom . import manager_info\n\nif TYPE_CHECKING:\n    from .manager import ApiClient as _ApiClient\n    from .manager.models import (\n        AvailableJobType as _AvailableJobType,\n        Job as _Job,\n        SubmittedJob as _SubmittedJob,\n    )\nelse:\n    _AvailableJobType = object\n    _ApiClient = object\n    _Job = object\n    _SubmittedJob = object\n\n\n# If a job has a setting with this key, type 'str', and subtype 'file_path',\n# it'll be set to the path of the BAT-packed blend file.\nBLENDFILE_SETTING_KEY = \"blendfile\"\n\nlog = logging.getLogger(__name__)\n\n\ndef job_for_scene(scene: bpy.types.Scene) -\u003e Optional[_SubmittedJob]:\n    ",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "from pathlib import Path, PurePosixPath"
          },
          {
            "lineNumber": 3,
            "text": "from typing import TYPE_CHECKING, Optional, Union"
          },
          {
            "lineNumber": 4,
            "text": "import platform"
          },
          {
            "lineNumber": 5,
            "text": "import logging"
          },
          {
            "lineNumber": 6
          },
          {
            "lineNumber": 7,
            "text": "import bpy"
          },
          {
            "lineNumber": 8
          },
          {
            "lineNumber": 9,
            "text": "from .job_types_propgroup import JobTypePropertyGroup"
          },
          {
            "lineNumber": 10,
            "text": "from .bat.submodules import bpathlib"
          },
          {
            "lineNumber": 11,
            "text": "from . import manager_info"
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 14,
            "text": "    from .manager import ApiClient as _ApiClient"
          },
          {
            "lineNumber": 15,
            "text": "    from .manager.models import ("
          },
          {
            "lineNumber": 16,
            "text": "        AvailableJobType as _AvailableJobType,"
          },
          {
            "lineNumber": 17,
            "text": "        Job as _Job,"
          },
          {
            "lineNumber": 18,
            "text": "        SubmittedJob as _SubmittedJob,"
          },
          {
            "lineNumber": 19,
            "text": "    )"
          },
          {
            "lineNumber": 20,
            "text": "else:"
          },
          {
            "lineNumber": 21,
            "text": "    _AvailableJobType = object"
          },
          {
            "lineNumber": 22,
            "text": "    _ApiClient = object"
          },
          {
            "lineNumber": 23,
            "text": "    _Job = object"
          },
          {
            "lineNumber": 24,
            "text": "    _SubmittedJob = object"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26
          },
          {
            "lineNumber": 27,
            "text": "# If a job has a setting with this key, type 'str', and subtype 'file_path',"
          },
          {
            "lineNumber": 28,
            "text": "# it'll be set to the path of the BAT-packed blend file."
          },
          {
            "lineNumber": 29,
            "text": "BLENDFILE_SETTING_KEY = \"blendfile\""
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 32
          },
          {
            "lineNumber": 33
          },
          {
            "isSignature": true,
            "lineNumber": 34,
            "text": "def job_for_scene(scene: bpy.types.Scene) -\u003e Optional[_SubmittedJob]:"
          },
          {
            "lineNumber": 35,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 68
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.4165422022342682
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:\n\n\n        if manager.shared_storage.shaman_enabled:\n            # self.blendfile_on_farm will be set when BAT created the checkout,\n            # see _on_bat_pack_msg() below.\n            self.blendfile_on_farm = None\n            self._bat_pack_shaman(context, blendfile)\n        elif job_submission.is_file_inside_job_storage(context, blendfile):\n            self.log.info(\n                \"File is already in job storage location, submitting it as-is\"\n            )\n            self._use_blendfile_directly(context, blendfile)\n        else:\n            self.log.info(\n                \"File is not already in job storage location, copying it there\"\n            )\n            try:\n                self.blendfile_on_farm = self._bat_pack_filesystem(context, blendfile)\n            except FileNotFoundError:\n                self._quit(context)\n                return False\n\n        wm = context.window_manager\n        self.timer = wm.event_timer_add(self.TIMER_PERIOD, window=context.window)\n\n        return True\n\n    def _bat_pack_filesystem(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e PurePosixPath:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 366,
            "text": "def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 381
          },
          {
            "lineNumber": 382
          },
          {
            "lineNumber": 383,
            "text": "        if manager.shared_storage.shaman_enabled:"
          },
          {
            "lineNumber": 384,
            "text": "            # self.blendfile_on_farm will be set when BAT created the checkout,"
          },
          {
            "lineNumber": 385,
            "text": "            # see _on_bat_pack_msg() below."
          },
          {
            "lineNumber": 386,
            "text": "            self.blendfile_on_farm = None"
          },
          {
            "lineNumber": 387,
            "text": "            self._bat_pack_shaman(context, blendfile)"
          },
          {
            "lineNumber": 388,
            "text": "        elif job_submission.is_file_inside_job_storage(context, blendfile):"
          },
          {
            "lineNumber": 389,
            "text": "            self.log.info("
          },
          {
            "lineNumber": 390,
            "text": "                \"File is already in job storage location, submitting it as-is\""
          },
          {
            "lineNumber": 391,
            "text": "            )"
          },
          {
            "lineNumber": 392,
            "text": "            self._use_blendfile_directly(context, blendfile)"
          },
          {
            "lineNumber": 393,
            "text": "        else:"
          },
          {
            "lineNumber": 394,
            "text": "            self.log.info("
          },
          {
            "lineNumber": 395,
            "text": "                \"File is not already in job storage location, copying it there\""
          },
          {
            "lineNumber": 396,
            "text": "            )"
          },
          {
            "lineNumber": 397,
            "text": "            try:"
          },
          {
            "lineNumber": 398,
            "text": "                self.blendfile_on_farm = self._bat_pack_filesystem(context, blendfile)"
          },
          {
            "lineNumber": 399,
            "text": "            except FileNotFoundError:"
          },
          {
            "lineNumber": 400,
            "text": "                self._quit(context)"
          },
          {
            "lineNumber": 401,
            "text": "                return False"
          },
          {
            "lineNumber": 402
          },
          {
            "lineNumber": 403,
            "text": "        wm = context.window_manager"
          },
          {
            "lineNumber": 404,
            "text": "        self.timer = wm.event_timer_add(self.TIMER_PERIOD, window=context.window)"
          },
          {
            "lineNumber": 405
          },
          {
            "lineNumber": 406,
            "text": "        return True"
          },
          {
            "lineNumber": 407
          },
          {
            "lineNumber": 408,
            "text": "    def _bat_pack_filesystem("
          },
          {
            "lineNumber": 409,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 410,
            "text": "    ) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 411,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 301
              },
              "startPosition": {
                "column": 5,
                "line": 300
              }
            }
          ]
        }
      },
      "score": 0.41513383388519287
    },
    {
      "codeBlock": {
        "contents": "_running_packthread: typing.Optional[PackThread] = None\n_packer_lock = threading.RLock()\n\n\ndef copy(  # type: ignore\n    base_blendfile: Path,\n    project: Path,\n    target: str,\n    exclusion_filter: str,\n    *,\n    relative_only: bool,\n    packer_class=submodules.pack.Packer,\n    packer_kwargs: Optional[dict[Any, Any]] = None,\n) -\u003e PackThread:\n    \"\"\"Use BAT to copy the given file and dependencies to the target location.\n\n    Runs BAT in a separate thread, and returns early. Use poll() to get updates\n    \u0026 the final result.\n    \"\"\"\n    global _running_packthread\n\n    with _packer_lock:\n        if _running_packthread is not None:\n            raise RuntimeError(\"other packing operation already in progress\")\n\n    # Due to issues with library overrides and unsynced pointers, it's quite\n    # common for the Blender Animation Studio to get crashes of BAT. To avoid\n    # these, Strict Pointer Mode is disabled.\n    submodules.blendfile.set_strict_pointer_mode(False)\n\n    log.info(\"BAT pack parameters:\")\n    log.info(\"base_blendfile = %r\", base_blendfile)\n    log.info(\"project        = %r\", project)\n    log.info(\"target         = %r\", target)\n\n    if packer_kwargs is None:\n        packer_kwargs = {}\n    packer = packer_class(\n        base_blendfile,\n        project,\n        target,\n        compress=True,\n        relative_only=relative_only,\n        **packer_kwargs,\n    )\n    if exclusion_filter:\n        filter_parts = exclusion_filter.strip().split(\" \")\n        packer.exclude(*filter_parts)\n\n    packthread = PackThread(packer=packer)\n    with _packer_lock:\n        _running_packthread = packthread\n\n    packthread.start()\n    return packthread\n\n\ndef abort() -\u003e None:\n    \"\"\"Abort a running copy() call.\n\n    No-op when there is no running copy(). Can be called from any thread.\n    ",
        "detailedLines": [
          {
            "lineNumber": 197,
            "text": "_running_packthread: typing.Optional[PackThread] = None"
          },
          {
            "lineNumber": 198,
            "text": "_packer_lock = threading.RLock()"
          },
          {
            "lineNumber": 199
          },
          {
            "lineNumber": 200
          },
          {
            "isSignature": true,
            "lineNumber": 201,
            "text": "def copy(  # type: ignore"
          },
          {
            "isSignature": true,
            "lineNumber": 202,
            "text": "    base_blendfile: Path,"
          },
          {
            "isSignature": true,
            "lineNumber": 203,
            "text": "    project: Path,"
          },
          {
            "isSignature": true,
            "lineNumber": 204,
            "text": "    target: str,"
          },
          {
            "isSignature": true,
            "lineNumber": 205,
            "text": "    exclusion_filter: str,"
          },
          {
            "isSignature": true,
            "lineNumber": 206,
            "text": "    *,"
          },
          {
            "isSignature": true,
            "lineNumber": 207,
            "text": "    relative_only: bool,"
          },
          {
            "isSignature": true,
            "lineNumber": 208,
            "text": "    packer_class=submodules.pack.Packer,"
          },
          {
            "isSignature": true,
            "lineNumber": 209,
            "text": "    packer_kwargs: Optional[dict[Any, Any]] = None,"
          },
          {
            "isSignature": true,
            "lineNumber": 210,
            "text": ") -\u003e PackThread:"
          },
          {
            "lineNumber": 211,
            "text": "    \"\"\"Use BAT to copy the given file and dependencies to the target location."
          },
          {
            "lineNumber": 212
          },
          {
            "lineNumber": 213,
            "text": "    Runs BAT in a separate thread, and returns early. Use poll() to get updates"
          },
          {
            "lineNumber": 214,
            "text": "    \u0026 the final result."
          },
          {
            "lineNumber": 215,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 216,
            "text": "    global _running_packthread"
          },
          {
            "lineNumber": 217
          },
          {
            "lineNumber": 218,
            "text": "    with _packer_lock:"
          },
          {
            "lineNumber": 219,
            "text": "        if _running_packthread is not None:"
          },
          {
            "lineNumber": 220,
            "text": "            raise RuntimeError(\"other packing operation already in progress\")"
          },
          {
            "lineNumber": 221
          },
          {
            "lineNumber": 222,
            "text": "    # Due to issues with library overrides and unsynced pointers, it's quite"
          },
          {
            "lineNumber": 223,
            "text": "    # common for the Blender Animation Studio to get crashes of BAT. To avoid"
          },
          {
            "lineNumber": 224,
            "text": "    # these, Strict Pointer Mode is disabled."
          },
          {
            "lineNumber": 225,
            "text": "    submodules.blendfile.set_strict_pointer_mode(False)"
          },
          {
            "lineNumber": 226
          },
          {
            "lineNumber": 227,
            "text": "    log.info(\"BAT pack parameters:\")"
          },
          {
            "lineNumber": 228,
            "text": "    log.info(\"base_blendfile = %r\", base_blendfile)"
          },
          {
            "lineNumber": 229,
            "text": "    log.info(\"project        = %r\", project)"
          },
          {
            "lineNumber": 230,
            "text": "    log.info(\"target         = %r\", target)"
          },
          {
            "lineNumber": 231
          },
          {
            "lineNumber": 232,
            "text": "    if packer_kwargs is None:"
          },
          {
            "lineNumber": 233,
            "text": "        packer_kwargs = {}"
          },
          {
            "lineNumber": 234,
            "text": "    packer = packer_class("
          },
          {
            "lineNumber": 235,
            "text": "        base_blendfile,"
          },
          {
            "lineNumber": 236,
            "text": "        project,"
          },
          {
            "lineNumber": 237,
            "text": "        target,"
          },
          {
            "lineNumber": 238,
            "text": "        compress=True,"
          },
          {
            "lineNumber": 239,
            "text": "        relative_only=relative_only,"
          },
          {
            "lineNumber": 240,
            "text": "        **packer_kwargs,"
          },
          {
            "lineNumber": 241,
            "text": "    )"
          },
          {
            "lineNumber": 242,
            "text": "    if exclusion_filter:"
          },
          {
            "lineNumber": 243,
            "text": "        filter_parts = exclusion_filter.strip().split(\" \")"
          },
          {
            "lineNumber": 244,
            "text": "        packer.exclude(*filter_parts)"
          },
          {
            "lineNumber": 245
          },
          {
            "lineNumber": 246,
            "text": "    packthread = PackThread(packer=packer)"
          },
          {
            "lineNumber": 247,
            "text": "    with _packer_lock:"
          },
          {
            "lineNumber": 248,
            "text": "        _running_packthread = packthread"
          },
          {
            "lineNumber": 249
          },
          {
            "lineNumber": 250,
            "text": "    packthread.start()"
          },
          {
            "isSignature": true,
            "lineNumber": 251,
            "text": "    return packthread"
          },
          {
            "lineNumber": 252
          },
          {
            "lineNumber": 253
          },
          {
            "isSignature": true,
            "lineNumber": 254,
            "text": "def abort() -\u003e None:"
          },
          {
            "lineNumber": 255,
            "text": "    \"\"\"Abort a running copy() call."
          },
          {
            "lineNumber": 256
          },
          {
            "lineNumber": 257,
            "text": "    No-op when there is no running copy(). Can be called from any thread."
          },
          {
            "lineNumber": 258,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 264
          },
          "startPosition": {
            "column": 27,
            "line": 193
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.40649938583374023
    },
    {
      "codeBlock": {
        "contents": "def is_file_inside_job_storage(context: bpy.types.Context, blendfile: Path) -\u003e bool:\n    \"\"\"Check whether current blend file is inside the storage path.\n\n    :return: True when the current blend file is inside the Flamenco job storage\n        directory already. In this case it won't be BAT-packed, as it's assumed\n        the job storage dir is accessible by the workers already.\n    \"\"\"\n\n    blendfile = bpathlib.make_absolute(blendfile)\n\n    info = manager_info.load_cached()\n    if not info:\n        raise RuntimeError(\"Flamenco Manager info unknown, please refresh.\")\n\n    job_storage = bpathlib.make_absolute(Path(info.shared_storage.location))\n\n    log.info(\"Checking whether the file is already inside the job storage\")\n    log.info(\"    file   : %s\", blendfile)\n    log.info(\"    storage: %s\", job_storage)\n\n    try:\n        blendfile.relative_to(job_storage)\n    except ValueError:\n        return False\n    return True",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 134,
            "text": "def is_file_inside_job_storage(context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 135,
            "text": "    \"\"\"Check whether current blend file is inside the storage path."
          },
          {
            "lineNumber": 136
          },
          {
            "lineNumber": 137,
            "text": "    :return: True when the current blend file is inside the Flamenco job storage"
          },
          {
            "lineNumber": 138,
            "text": "        directory already. In this case it won't be BAT-packed, as it's assumed"
          },
          {
            "lineNumber": 139,
            "text": "        the job storage dir is accessible by the workers already."
          },
          {
            "lineNumber": 140,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 141
          },
          {
            "lineNumber": 142,
            "text": "    blendfile = bpathlib.make_absolute(blendfile)"
          },
          {
            "lineNumber": 143
          },
          {
            "lineNumber": 144,
            "text": "    info = manager_info.load_cached()"
          },
          {
            "lineNumber": 145,
            "text": "    if not info:"
          },
          {
            "lineNumber": 146,
            "text": "        raise RuntimeError(\"Flamenco Manager info unknown, please refresh.\")"
          },
          {
            "lineNumber": 147
          },
          {
            "lineNumber": 148,
            "text": "    job_storage = bpathlib.make_absolute(Path(info.shared_storage.location))"
          },
          {
            "lineNumber": 149
          },
          {
            "lineNumber": 150,
            "text": "    log.info(\"Checking whether the file is already inside the job storage\")"
          },
          {
            "lineNumber": 151,
            "text": "    log.info(\"    file   : %s\", blendfile)"
          },
          {
            "lineNumber": 152,
            "text": "    log.info(\"    storage: %s\", job_storage)"
          },
          {
            "lineNumber": 153
          },
          {
            "lineNumber": 154,
            "text": "    try:"
          },
          {
            "lineNumber": 155,
            "text": "        blendfile.relative_to(job_storage)"
          },
          {
            "lineNumber": 156,
            "text": "    except ValueError:"
          },
          {
            "lineNumber": 157,
            "text": "        return False"
          },
          {
            "isSignature": true,
            "lineNumber": 158,
            "text": "    return True"
          }
        ],
        "range": {
          "endPosition": {
            "column": 15,
            "line": 157
          },
          "startPosition": {
            "column": 42,
            "line": 130
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.40479031205177307
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _save_blendfile(self, context):\n\n\n        try:\n            # The file extension should be determined by the render settings, not necessarily\n            # by the settings in the output panel.\n            render.use_file_extension = True\n\n            # Rescheduling should not overwrite existing frames.\n            render.use_overwrite = False\n            render.use_placeholder = False\n\n            # To work around a shortcoming of BAT, ensure that all\n            # indirectly-linked data is still saved as directly-linked.\n            #\n            # See &#96;133dde41bb5b: Improve handling of (in)directly linked status\n            # for linked IDs&#96; in Blender's Git repository.\n            if old_use_all_linked_data_direct is not None:\n                self.log.info(\n                    \"Overriding prefs.experimental.use_all_linked_data_direct = True\"\n                )\n                prefs.experimental.use_all_linked_data_direct = True\n\n            filepath = Path(context.blend_data.filepath)\n            if job_submission.is_file_inside_job_storage(context, filepath):\n                self.log.info(\n                    \"Saving blendfile, already in shared storage: %s\", filepath\n                )\n                bpy.ops.wm.save_as_mainfile()\n            else:\n                filepath = filepath.with_suffix(\".flamenco.blend\")\n                self.log.info(\"Saving copy to temporary file %s\", filepath)\n                bpy.ops.wm.save_as_mainfile(\n                    filepath=str(filepath), compress=True, copy=True\n                )\n            self.temp_blendfile = filepath\n        finally:\n            # Restore the settings we changed, even after an exception.\n            render.use_file_extension = old_use_file_extension",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 300,
            "text": "def _save_blendfile(self, context):"
          },
          {
            "lineNumber": 317
          },
          {
            "lineNumber": 318
          },
          {
            "lineNumber": 319,
            "text": "        try:"
          },
          {
            "lineNumber": 320,
            "text": "            # The file extension should be determined by the render settings, not necessarily"
          },
          {
            "lineNumber": 321,
            "text": "            # by the settings in the output panel."
          },
          {
            "lineNumber": 322,
            "text": "            render.use_file_extension = True"
          },
          {
            "lineNumber": 323
          },
          {
            "lineNumber": 324,
            "text": "            # Rescheduling should not overwrite existing frames."
          },
          {
            "lineNumber": 325,
            "text": "            render.use_overwrite = False"
          },
          {
            "lineNumber": 326,
            "text": "            render.use_placeholder = False"
          },
          {
            "lineNumber": 327
          },
          {
            "lineNumber": 328,
            "text": "            # To work around a shortcoming of BAT, ensure that all"
          },
          {
            "lineNumber": 329,
            "text": "            # indirectly-linked data is still saved as directly-linked."
          },
          {
            "lineNumber": 330,
            "text": "            #"
          },
          {
            "lineNumber": 331,
            "text": "            # See &#96;133dde41bb5b: Improve handling of (in)directly linked status"
          },
          {
            "lineNumber": 332,
            "text": "            # for linked IDs&#96; in Blender's Git repository."
          },
          {
            "lineNumber": 333,
            "text": "            if old_use_all_linked_data_direct is not None:"
          },
          {
            "lineNumber": 334,
            "text": "                self.log.info("
          },
          {
            "lineNumber": 335,
            "text": "                    \"Overriding prefs.experimental.use_all_linked_data_direct = True\""
          },
          {
            "lineNumber": 336,
            "text": "                )"
          },
          {
            "lineNumber": 337,
            "text": "                prefs.experimental.use_all_linked_data_direct = True"
          },
          {
            "lineNumber": 338
          },
          {
            "lineNumber": 339,
            "text": "            filepath = Path(context.blend_data.filepath)"
          },
          {
            "lineNumber": 340,
            "text": "            if job_submission.is_file_inside_job_storage(context, filepath):"
          },
          {
            "lineNumber": 341,
            "text": "                self.log.info("
          },
          {
            "lineNumber": 342,
            "text": "                    \"Saving blendfile, already in shared storage: %s\", filepath"
          },
          {
            "lineNumber": 343,
            "text": "                )"
          },
          {
            "lineNumber": 344,
            "text": "                bpy.ops.wm.save_as_mainfile()"
          },
          {
            "lineNumber": 345,
            "text": "            else:"
          },
          {
            "lineNumber": 346,
            "text": "                filepath = filepath.with_suffix(\".flamenco.blend\")"
          },
          {
            "lineNumber": 347,
            "text": "                self.log.info(\"Saving copy to temporary file %s\", filepath)"
          },
          {
            "lineNumber": 348,
            "text": "                bpy.ops.wm.save_as_mainfile("
          },
          {
            "lineNumber": 349,
            "text": "                    filepath=str(filepath), compress=True, copy=True"
          },
          {
            "lineNumber": 350,
            "text": "                )"
          },
          {
            "lineNumber": 351,
            "text": "            self.temp_blendfile = filepath"
          },
          {
            "lineNumber": 352,
            "text": "        finally:"
          },
          {
            "lineNumber": 353,
            "text": "            # Restore the settings we changed, even after an exception."
          },
          {
            "lineNumber": 354,
            "text": "            render.use_file_extension = old_use_file_extension"
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 301
              },
              "startPosition": {
                "column": 5,
                "line": 300
              }
            }
          ]
        }
      },
      "score": 0.3999325931072235
    },
    {
      "codeBlock": {
        "contents": "class FlamencoPreferences(bpy.types.AddonPreferences):\n    bl_idname = \"flamenco\"\n\n    manager_url: bpy.props.StringProperty(  # type: ignore\n        name=\"Manager URL\",\n        description=\"Location of the Manager\",\n        default=\"http://localhost:8080/\",\n        update=_manager_url_updated,\n    )\n\n    project_finder: bpy.props.EnumProperty(  # type: ignore\n        name=\"Project Finder\",\n        description=\"Strategy for Flamenco to find the top level directory of your project\",\n        default=_project_finder_enum_items[0][0],\n        items=_project_finder_enum_items,\n    )\n\n    # Property that gets its value from the above _job_storage, and cannot be\n    # set. This makes it read-only in the GUI.\n    job_storage_for_gui: bpy.props.StringProperty(  # type: ignore\n        name=\"Job Storage Directory\",\n        subtype=\"DIR_PATH\",\n        default=\"\",\n        options={\"SKIP_SAVE\"},\n        description=\"Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager\",\n        get=lambda prefs: prefs._job_storage(),\n    )\n\n    def draw(self, context: bpy.types.Context) -\u003e None:\n        layout = self.layout",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 51,
            "text": "class FlamencoPreferences(bpy.types.AddonPreferences):"
          },
          {
            "lineNumber": 52,
            "text": "    bl_idname = \"flamenco\""
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54,
            "text": "    manager_url: bpy.props.StringProperty(  # type: ignore"
          },
          {
            "lineNumber": 55,
            "text": "        name=\"Manager URL\","
          },
          {
            "lineNumber": 56,
            "text": "        description=\"Location of the Manager\","
          },
          {
            "lineNumber": 57,
            "text": "        default=\"http://localhost:8080/\","
          },
          {
            "lineNumber": 58,
            "text": "        update=_manager_url_updated,"
          },
          {
            "lineNumber": 59,
            "text": "    )"
          },
          {
            "lineNumber": 60
          },
          {
            "lineNumber": 61,
            "text": "    project_finder: bpy.props.EnumProperty(  # type: ignore"
          },
          {
            "lineNumber": 62,
            "text": "        name=\"Project Finder\","
          },
          {
            "lineNumber": 63,
            "text": "        description=\"Strategy for Flamenco to find the top level directory of your project\","
          },
          {
            "lineNumber": 64,
            "text": "        default=_project_finder_enum_items[0][0],"
          },
          {
            "lineNumber": 65,
            "text": "        items=_project_finder_enum_items,"
          },
          {
            "lineNumber": 66,
            "text": "    )"
          },
          {
            "lineNumber": 67
          },
          {
            "lineNumber": 68,
            "text": "    # Property that gets its value from the above _job_storage, and cannot be"
          },
          {
            "lineNumber": 69,
            "text": "    # set. This makes it read-only in the GUI."
          },
          {
            "lineNumber": 70,
            "text": "    job_storage_for_gui: bpy.props.StringProperty(  # type: ignore"
          },
          {
            "lineNumber": 71,
            "text": "        name=\"Job Storage Directory\","
          },
          {
            "lineNumber": 72,
            "text": "        subtype=\"DIR_PATH\","
          },
          {
            "lineNumber": 73,
            "text": "        default=\"\","
          },
          {
            "lineNumber": 74,
            "text": "        options={\"SKIP_SAVE\"},"
          },
          {
            "lineNumber": 75,
            "text": "        description=\"Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager\","
          },
          {
            "lineNumber": 76,
            "text": "        get=lambda prefs: prefs._job_storage(),"
          },
          {
            "lineNumber": 77,
            "text": "    )"
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "    def draw(self, context: bpy.types.Context) -\u003e None:"
          },
          {
            "lineNumber": 80,
            "text": "        layout = self.layout"
          }
        ],
        "range": {
          "endPosition": {
            "line": 136
          },
          "startPosition": {
            "column": 77,
            "line": 47
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\preferences.py",
        "signatures": {}
      },
      "score": 0.39617109298706055
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n    bl_idname = \"flamenco.submit_job\"\n    bl_label = \"Flamenco: Submit Job\"\n    bl_description = \"Pack the current blend file and send it to Flamenco\"\n    bl_options = {\"REGISTER\"}  # No UNDO.\n\n    blendfile_on_farm: Optional[PurePosixPath] = None\n    actual_shaman_checkout_path: Optional[PurePosixPath] = None\n\n    job_name: bpy.props.StringProperty(name=\"Job Name\")  # type: ignore\n    job: Optional[_SubmittedJob] = None\n    temp_blendfile: Optional[Path] = None\n    ignore_version_mismatch: bpy.props.BoolProperty(  # type: ignore\n        name=\"Ignore Version Mismatch\",\n        default=False,\n    )\n\n    TIMER_PERIOD = 0.25  # seconds\n    timer: Optional[bpy.types.Timer] = None\n    packthread: Optional[_PackThread] = None\n\n    log = _log.getChild(bl_idname)\n\n    @classmethod\n    def poll(cls, context: bpy.types.Context) -\u003e bool:\n        # Only allow submission when there is a job type selected.\n        job_type = job_types.active_job_type(context.scene)\n        cls.poll_message_set(\"No job type selected\")\n        return job_type is not None\n\n    def execute(self, context: bpy.types.Context) -\u003e set[str]:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 103,
            "text": "    bl_idname = \"flamenco.submit_job\""
          },
          {
            "lineNumber": 104,
            "text": "    bl_label = \"Flamenco: Submit Job\""
          },
          {
            "lineNumber": 105,
            "text": "    bl_description = \"Pack the current blend file and send it to Flamenco\""
          },
          {
            "lineNumber": 106,
            "text": "    bl_options = {\"REGISTER\"}  # No UNDO."
          },
          {
            "lineNumber": 107
          },
          {
            "lineNumber": 108,
            "text": "    blendfile_on_farm: Optional[PurePosixPath] = None"
          },
          {
            "lineNumber": 109,
            "text": "    actual_shaman_checkout_path: Optional[PurePosixPath] = None"
          },
          {
            "lineNumber": 110
          },
          {
            "lineNumber": 111,
            "text": "    job_name: bpy.props.StringProperty(name=\"Job Name\")  # type: ignore"
          },
          {
            "lineNumber": 112,
            "text": "    job: Optional[_SubmittedJob] = None"
          },
          {
            "lineNumber": 113,
            "text": "    temp_blendfile: Optional[Path] = None"
          },
          {
            "lineNumber": 114,
            "text": "    ignore_version_mismatch: bpy.props.BoolProperty(  # type: ignore"
          },
          {
            "lineNumber": 115,
            "text": "        name=\"Ignore Version Mismatch\","
          },
          {
            "lineNumber": 116,
            "text": "        default=False,"
          },
          {
            "lineNumber": 117,
            "text": "    )"
          },
          {
            "lineNumber": 118
          },
          {
            "lineNumber": 119,
            "text": "    TIMER_PERIOD = 0.25  # seconds"
          },
          {
            "lineNumber": 120,
            "text": "    timer: Optional[bpy.types.Timer] = None"
          },
          {
            "lineNumber": 121,
            "text": "    packthread: Optional[_PackThread] = None"
          },
          {
            "lineNumber": 122
          },
          {
            "lineNumber": 123,
            "text": "    log = _log.getChild(bl_idname)"
          },
          {
            "lineNumber": 124
          },
          {
            "lineNumber": 125,
            "text": "    @classmethod"
          },
          {
            "lineNumber": 126,
            "text": "    def poll(cls, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 127,
            "text": "        # Only allow submission when there is a job type selected."
          },
          {
            "lineNumber": 128,
            "text": "        job_type = job_types.active_job_type(context.scene)"
          },
          {
            "lineNumber": 129,
            "text": "        cls.poll_message_set(\"No job type selected\")"
          },
          {
            "lineNumber": 130,
            "text": "        return job_type is not None"
          },
          {
            "lineNumber": 131
          },
          {
            "lineNumber": 132,
            "text": "    def execute(self, context: bpy.types.Context) -\u003e set[str]:"
          },
          {
            "lineNumber": 133,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "column": 27,
            "line": 98
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {}
      },
      "score": 0.39531418681144714
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _save_blendfile(self, context):\n\n        finally:\n            # Restore the settings we changed, even after an exception.\n            render.use_file_extension = old_use_file_extension\n            render.use_overwrite = old_use_overwrite\n            render.use_placeholder = old_use_placeholder\n\n            # Only restore if the property exists to begin with:\n            if old_use_all_linked_data_direct is not None:\n                prefs.experimental.use_all_linked_data_direct = (\n                    old_use_all_linked_data_direct\n                )\n\n        return filepath\n\n    def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:\n        \"\"\"Ensure that the files are somewhere in the shared storage.\n\n        Returns True if a packing thread has been started, and False otherwise.\n        \"\"\"\n\n        from .bat import interface as bat_interface\n\n        if bat_interface.is_packing():\n            self.report({\"ERROR\"}, \"Another packing operation is running\")\n            self._quit(context)\n            return False\n\n        manager = self._manager_info(context)\n        if not manager:\n            return False\n\n        if manager.shared_storage.shaman_enabled:",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 300,
            "text": "def _save_blendfile(self, context):"
          },
          {
            "lineNumber": 351
          },
          {
            "lineNumber": 352,
            "text": "        finally:"
          },
          {
            "lineNumber": 353,
            "text": "            # Restore the settings we changed, even after an exception."
          },
          {
            "lineNumber": 354,
            "text": "            render.use_file_extension = old_use_file_extension"
          },
          {
            "lineNumber": 355,
            "text": "            render.use_overwrite = old_use_overwrite"
          },
          {
            "lineNumber": 356,
            "text": "            render.use_placeholder = old_use_placeholder"
          },
          {
            "lineNumber": 357
          },
          {
            "lineNumber": 358,
            "text": "            # Only restore if the property exists to begin with:"
          },
          {
            "lineNumber": 359,
            "text": "            if old_use_all_linked_data_direct is not None:"
          },
          {
            "lineNumber": 360,
            "text": "                prefs.experimental.use_all_linked_data_direct = ("
          },
          {
            "lineNumber": 361,
            "text": "                    old_use_all_linked_data_direct"
          },
          {
            "lineNumber": 362,
            "text": "                )"
          },
          {
            "lineNumber": 363
          },
          {
            "lineNumber": 364,
            "text": "        return filepath"
          },
          {
            "lineNumber": 365
          },
          {
            "lineNumber": 366,
            "text": "    def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 367,
            "text": "        \"\"\"Ensure that the files are somewhere in the shared storage."
          },
          {
            "lineNumber": 368
          },
          {
            "lineNumber": 369,
            "text": "        Returns True if a packing thread has been started, and False otherwise."
          },
          {
            "lineNumber": 370,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 371
          },
          {
            "lineNumber": 372,
            "text": "        from .bat import interface as bat_interface"
          },
          {
            "lineNumber": 373
          },
          {
            "lineNumber": 374,
            "text": "        if bat_interface.is_packing():"
          },
          {
            "lineNumber": 375,
            "text": "            self.report({\"ERROR\"}, \"Another packing operation is running\")"
          },
          {
            "lineNumber": 376,
            "text": "            self._quit(context)"
          },
          {
            "lineNumber": 377,
            "text": "            return False"
          },
          {
            "lineNumber": 378
          },
          {
            "lineNumber": 379,
            "text": "        manager = self._manager_info(context)"
          },
          {
            "lineNumber": 380,
            "text": "        if not manager:"
          },
          {
            "lineNumber": 381,
            "text": "            return False"
          },
          {
            "lineNumber": 382
          },
          {
            "lineNumber": 383,
            "text": "        if manager.shared_storage.shaman_enabled:"
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 301
              },
              "startPosition": {
                "column": 5,
                "line": 300
              }
            }
          ]
        }
      },
      "score": 0.3889443874359131
    },
    {
      "codeBlock": {
        "contents": "class FlamencoPreferences(bpy.types.AddonPreferences):\n\n\n    def project_root(self) -\u003e Path:\n        \"\"\"Use the configured project finder to find the project root directory.\"\"\"\n\n        if not self.project_finder:\n            # Just a sanity fallback for missing preferences. It should be\n            # covered by the 'default=...' of the property, but just to be sure.\n            self.project_finder = \"BLENDER_PROJECT\"\n\n        # It is assumed that the blendfile is saved.\n        blendfile = Path(bpy.data.filepath)\n        return projects.for_blendfile(blendfile, self.project_finder)\n\n    def _job_storage(self) -\u003e str:\n        info = manager_info.load_cached()\n        if not info:\n            return \"Unknown, refresh first.\"\n        return str(info.shared_storage.location)\n\n\ndef get(context: bpy.types.Context) -\u003e FlamencoPreferences:\n    \"\"\"Return the add-on preferences.\"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 51,
            "text": "class FlamencoPreferences(bpy.types.AddonPreferences):"
          },
          {
            "lineNumber": 119
          },
          {
            "lineNumber": 120
          },
          {
            "lineNumber": 121,
            "text": "    def project_root(self) -\u003e Path:"
          },
          {
            "lineNumber": 122,
            "text": "        \"\"\"Use the configured project finder to find the project root directory.\"\"\""
          },
          {
            "lineNumber": 123
          },
          {
            "lineNumber": 124,
            "text": "        if not self.project_finder:"
          },
          {
            "lineNumber": 125,
            "text": "            # Just a sanity fallback for missing preferences. It should be"
          },
          {
            "lineNumber": 126,
            "text": "            # covered by the 'default=...' of the property, but just to be sure."
          },
          {
            "lineNumber": 127,
            "text": "            self.project_finder = \"BLENDER_PROJECT\""
          },
          {
            "lineNumber": 128
          },
          {
            "lineNumber": 129,
            "text": "        # It is assumed that the blendfile is saved."
          },
          {
            "lineNumber": 130,
            "text": "        blendfile = Path(bpy.data.filepath)"
          },
          {
            "lineNumber": 131,
            "text": "        return projects.for_blendfile(blendfile, self.project_finder)"
          },
          {
            "lineNumber": 132
          },
          {
            "lineNumber": 133,
            "text": "    def _job_storage(self) -\u003e str:"
          },
          {
            "lineNumber": 134,
            "text": "        info = manager_info.load_cached()"
          },
          {
            "lineNumber": 135,
            "text": "        if not info:"
          },
          {
            "lineNumber": 136,
            "text": "            return \"Unknown, refresh first.\""
          },
          {
            "isSignature": true,
            "lineNumber": 137,
            "text": "        return str(info.shared_storage.location)"
          },
          {
            "lineNumber": 138
          },
          {
            "lineNumber": 139
          },
          {
            "isSignature": true,
            "lineNumber": 140,
            "text": "def get(context: bpy.types.Context) -\u003e FlamencoPreferences:"
          },
          {
            "lineNumber": 141,
            "text": "    \"\"\"Return the add-on preferences.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 145
          },
          "startPosition": {
            "line": 50
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\preferences.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 52
              },
              "startPosition": {
                "column": 1,
                "line": 51
              }
            }
          ]
        }
      },
      "score": 0.38085469603538513
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n\n\n    def _manager_info(\n        self, context: bpy.types.Context\n    ) -\u003e Optional[manager_info.ManagerInfo]:\n        \"\"\"Load the manager info.\n\n        If it cannot be loaded, returns None after emitting an error message and\n        calling self._quit(context).\n        \"\"\"\n        manager = manager_info.load_cached()\n        if not manager:\n            self.report(\n                {\"ERROR\"}, \"No information known about Flamenco Manager, refresh first.\"\n            )\n            self._quit(context)\n            return None\n        return manager\n\n    def _presubmit_check(self, context: bpy.types.Context) -\u003e tuple[Path, bool]:\n        \"\"\"Do a pre-submission check, returning whether submission can continue.\n\n        Reports warnings when returning False, so the caller can just abort.\n\n        Returns a tuple (can_submit, filepath_to_submit)\n        \"\"\"\n\n        # Before doing anything, make sure the info we cached about the Manager\n        # is up to date. A change in job storage directory on the Manager can\n        # cause nasty error messages when we submit, and it's better to just be\n        # ahead of the curve and refresh first. This also allows for checking\n        # the actual Manager version before submitting.\n        err = self._check_manager(context)\n        if err:\n            self.report({\"WARNING\"}, err)\n            return Path(), False\n\n        if not context.blend_data.filepath:\n            # The file path needs to be known before the file can be submitted.\n            self.report(\n                {\"ERROR\"}, \"Please save your .blend file before submitting to Flamenco\"\n            )\n            return Path(), False\n\n        filepath = self._save_blendfile(context)\n\n        # Check the job with the Manager, to see if it would be accepted.\n        if not self._check_job(context):\n            return Path(), False\n\n        return filepath, True\n\n    def _save_blendfile(self, context):\n        \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 248
          },
          {
            "lineNumber": 249
          },
          {
            "lineNumber": 250,
            "text": "    def _manager_info("
          },
          {
            "lineNumber": 251,
            "text": "        self, context: bpy.types.Context"
          },
          {
            "lineNumber": 252,
            "text": "    ) -\u003e Optional[manager_info.ManagerInfo]:"
          },
          {
            "lineNumber": 253,
            "text": "        \"\"\"Load the manager info."
          },
          {
            "lineNumber": 254
          },
          {
            "lineNumber": 255,
            "text": "        If it cannot be loaded, returns None after emitting an error message and"
          },
          {
            "lineNumber": 256,
            "text": "        calling self._quit(context)."
          },
          {
            "lineNumber": 257,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 258,
            "text": "        manager = manager_info.load_cached()"
          },
          {
            "lineNumber": 259,
            "text": "        if not manager:"
          },
          {
            "lineNumber": 260,
            "text": "            self.report("
          },
          {
            "lineNumber": 261,
            "text": "                {\"ERROR\"}, \"No information known about Flamenco Manager, refresh first.\""
          },
          {
            "lineNumber": 262,
            "text": "            )"
          },
          {
            "lineNumber": 263,
            "text": "            self._quit(context)"
          },
          {
            "lineNumber": 264,
            "text": "            return None"
          },
          {
            "lineNumber": 265,
            "text": "        return manager"
          },
          {
            "lineNumber": 266
          },
          {
            "lineNumber": 267,
            "text": "    def _presubmit_check(self, context: bpy.types.Context) -\u003e tuple[Path, bool]:"
          },
          {
            "lineNumber": 268,
            "text": "        \"\"\"Do a pre-submission check, returning whether submission can continue."
          },
          {
            "lineNumber": 269
          },
          {
            "lineNumber": 270,
            "text": "        Reports warnings when returning False, so the caller can just abort."
          },
          {
            "lineNumber": 271
          },
          {
            "lineNumber": 272,
            "text": "        Returns a tuple (can_submit, filepath_to_submit)"
          },
          {
            "lineNumber": 273,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 274
          },
          {
            "lineNumber": 275,
            "text": "        # Before doing anything, make sure the info we cached about the Manager"
          },
          {
            "lineNumber": 276,
            "text": "        # is up to date. A change in job storage directory on the Manager can"
          },
          {
            "lineNumber": 277,
            "text": "        # cause nasty error messages when we submit, and it's better to just be"
          },
          {
            "lineNumber": 278,
            "text": "        # ahead of the curve and refresh first. This also allows for checking"
          },
          {
            "lineNumber": 279,
            "text": "        # the actual Manager version before submitting."
          },
          {
            "lineNumber": 280,
            "text": "        err = self._check_manager(context)"
          },
          {
            "lineNumber": 281,
            "text": "        if err:"
          },
          {
            "lineNumber": 282,
            "text": "            self.report({\"WARNING\"}, err)"
          },
          {
            "lineNumber": 283,
            "text": "            return Path(), False"
          },
          {
            "lineNumber": 284
          },
          {
            "lineNumber": 285,
            "text": "        if not context.blend_data.filepath:"
          },
          {
            "lineNumber": 286,
            "text": "            # The file path needs to be known before the file can be submitted."
          },
          {
            "lineNumber": 287,
            "text": "            self.report("
          },
          {
            "lineNumber": 288,
            "text": "                {\"ERROR\"}, \"Please save your .blend file before submitting to Flamenco\""
          },
          {
            "lineNumber": 289,
            "text": "            )"
          },
          {
            "lineNumber": 290,
            "text": "            return Path(), False"
          },
          {
            "lineNumber": 291
          },
          {
            "lineNumber": 292,
            "text": "        filepath = self._save_blendfile(context)"
          },
          {
            "lineNumber": 293
          },
          {
            "lineNumber": 294,
            "text": "        # Check the job with the Manager, to see if it would be accepted."
          },
          {
            "lineNumber": 295,
            "text": "        if not self._check_job(context):"
          },
          {
            "lineNumber": 296,
            "text": "            return Path(), False"
          },
          {
            "lineNumber": 297
          },
          {
            "lineNumber": 298,
            "text": "        return filepath, True"
          },
          {
            "lineNumber": 299
          },
          {
            "lineNumber": 300,
            "text": "    def _save_blendfile(self, context):"
          },
          {
            "lineNumber": 301,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.3803957998752594
    },
    {
      "codeBlock": {
        "contents": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore\n    \"\"\"Sends files to a Shaman server.\"\"\"\n\n    class AbortUpload(Exception):\n        \"\"\"Raised from the upload callback to abort an upload.\"\"\"\n\n    def __init__(\n        self,\n        api_client: _ApiClient,\n        local_project_root: Path,\n        checkout_path: str,\n    ) -\u003e None:\n        super().__init__()\n        from ..manager.apis import ShamanApi\n\n        self.shaman_api = ShamanApi(api_client)\n\n        self.project_root = local_project_root\n        self.checkout_path = checkout_path\n        self.log = log.getChild(self.__class__.__name__)\n        self.uploaded_files = 0\n        self.uploaded_bytes = 0\n\n        # Mapping from the relative path (as used in Shaman requests) to the\n        # absolute path where we can find the local file. This is typically just\n        # the same as the relative path (relative to the project root), but can\n        # also point to a temporary file when it had to be rewritten.\n        self._rel_to_local_path: dict[str, Path] = {}\n\n        # Temporary files that should be deleted before stopping.\n        self._delete_when_done: list[Path] = []\n\n    # noinspection PyBroadException\n    def run(self) -\u003e None:\n        try:\n            self._run()\n        except Exception as ex:\n            # We have to catch exceptions in a broad way, as this is running in\n            # a separate thread, and exceptions won't otherwise be seen.\n            self.log.exception(\"Error transferring files to Shaman\")\n            self.error_set(\"Unexpected exception transferring files to Shaman: %s\" % ex)\n        finally:\n            # Delete the files that were supposed to be moved.\n            for src in self._delete_when_done:\n                self.delete_file(src)\n            self._delete_when_done.clear()\n\n    def _run(self) -\u003e None:\n        self.uploaded_files = 0",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 99,
            "text": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore"
          },
          {
            "lineNumber": 100,
            "text": "    \"\"\"Sends files to a Shaman server.\"\"\""
          },
          {
            "lineNumber": 101
          },
          {
            "lineNumber": 102,
            "text": "    class AbortUpload(Exception):"
          },
          {
            "lineNumber": 103,
            "text": "        \"\"\"Raised from the upload callback to abort an upload.\"\"\""
          },
          {
            "lineNumber": 104
          },
          {
            "lineNumber": 105,
            "text": "    def __init__("
          },
          {
            "lineNumber": 106,
            "text": "        self,"
          },
          {
            "lineNumber": 107,
            "text": "        api_client: _ApiClient,"
          },
          {
            "lineNumber": 108,
            "text": "        local_project_root: Path,"
          },
          {
            "lineNumber": 109,
            "text": "        checkout_path: str,"
          },
          {
            "lineNumber": 110,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 111,
            "text": "        super().__init__()"
          },
          {
            "lineNumber": 112,
            "text": "        from ..manager.apis import ShamanApi"
          },
          {
            "lineNumber": 113
          },
          {
            "lineNumber": 114,
            "text": "        self.shaman_api = ShamanApi(api_client)"
          },
          {
            "lineNumber": 115
          },
          {
            "lineNumber": 116,
            "text": "        self.project_root = local_project_root"
          },
          {
            "lineNumber": 117,
            "text": "        self.checkout_path = checkout_path"
          },
          {
            "lineNumber": 118,
            "text": "        self.log = log.getChild(self.__class__.__name__)"
          },
          {
            "lineNumber": 119,
            "text": "        self.uploaded_files = 0"
          },
          {
            "lineNumber": 120,
            "text": "        self.uploaded_bytes = 0"
          },
          {
            "lineNumber": 121
          },
          {
            "lineNumber": 122,
            "text": "        # Mapping from the relative path (as used in Shaman requests) to the"
          },
          {
            "lineNumber": 123,
            "text": "        # absolute path where we can find the local file. This is typically just"
          },
          {
            "lineNumber": 124,
            "text": "        # the same as the relative path (relative to the project root), but can"
          },
          {
            "lineNumber": 125,
            "text": "        # also point to a temporary file when it had to be rewritten."
          },
          {
            "lineNumber": 126,
            "text": "        self._rel_to_local_path: dict[str, Path] = {}"
          },
          {
            "lineNumber": 127
          },
          {
            "lineNumber": 128,
            "text": "        # Temporary files that should be deleted before stopping."
          },
          {
            "lineNumber": 129,
            "text": "        self._delete_when_done: list[Path] = []"
          },
          {
            "lineNumber": 130
          },
          {
            "lineNumber": 131,
            "text": "    # noinspection PyBroadException"
          },
          {
            "lineNumber": 132,
            "text": "    def run(self) -\u003e None:"
          },
          {
            "lineNumber": 133,
            "text": "        try:"
          },
          {
            "lineNumber": 134,
            "text": "            self._run()"
          },
          {
            "lineNumber": 135,
            "text": "        except Exception as ex:"
          },
          {
            "lineNumber": 136,
            "text": "            # We have to catch exceptions in a broad way, as this is running in"
          },
          {
            "lineNumber": 137,
            "text": "            # a separate thread, and exceptions won't otherwise be seen."
          },
          {
            "lineNumber": 138,
            "text": "            self.log.exception(\"Error transferring files to Shaman\")"
          },
          {
            "lineNumber": 139,
            "text": "            self.error_set(\"Unexpected exception transferring files to Shaman: %s\" % ex)"
          },
          {
            "lineNumber": 140,
            "text": "        finally:"
          },
          {
            "lineNumber": 141,
            "text": "            # Delete the files that were supposed to be moved."
          },
          {
            "lineNumber": 142,
            "text": "            for src in self._delete_when_done:"
          },
          {
            "lineNumber": 143,
            "text": "                self.delete_file(src)"
          },
          {
            "lineNumber": 144,
            "text": "            self._delete_when_done.clear()"
          },
          {
            "lineNumber": 145
          },
          {
            "lineNumber": 146,
            "text": "    def _run(self) -\u003e None:"
          },
          {
            "lineNumber": 147,
            "text": "        self.uploaded_files = 0"
          }
        ],
        "range": {
          "endPosition": {
            "line": 473
          },
          "startPosition": {
            "column": 33,
            "line": 95
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.3778744339942932
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n@classmethod\ndef poll(cls, context: bpy.types.Context) -\u003e bool:\n        # Only allow submission when there is a job type selected.\n\n        return job_type is not None\n\n    def execute(self, context: bpy.types.Context) -\u003e set[str]:\n        filepath, ok = self._presubmit_check(context)\n        if not ok:\n            return {\"CANCELLED\"}\n\n        is_running = self._submit_files(context, filepath)\n        if not is_running:\n            return {\"CANCELLED\"}\n\n        if self.packthread is None:\n            # If there is no pack thread running, there isn't much we can do.\n            self.report({\"ERROR\"}, \"No pack thread running, please report a bug\")\n            self._quit(context)\n            return {\"CANCELLED\"}\n\n        # Keep handling messages from the background thread.\n        while True:\n            # Block for 5 seconds at a time. The exact duration doesn't matter,\n            # as this while-loop is blocking the main thread anyway.\n            msg = self.packthread.poll(timeout=5)\n            if not msg:\n                # No message received, is fine, just wait for another one.\n                continue\n\n            result = self._on_bat_pack_msg(context, msg)\n            if \"RUNNING_MODAL\" not in result:\n                break\n\n        self._quit(context)\n        self.packthread.join(timeout=5)\n        return {\"FINISHED\"}\n\n    def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -\u003e set[str]:\n\n        filepath, ok = self._presubmit_check(context)\n        if not ok:\n            return {\"CANCELLED\"}\n\n        is_running = self._submit_files(context, filepath)\n        if not is_running:\n            return {\"CANCELLED\"}\n\n        context.window_manager.modal_handler_add(self)\n        return {\"RUNNING_MODAL\"}\n\n    def modal(self, context: bpy.types.Context, event: bpy.types.Event) -\u003e set[str]:\n        # This function is called for TIMER events to poll the BAT pack thread.\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 125,
            "text": "@classmethod"
          },
          {
            "lineNumber": 126,
            "text": "def poll(cls, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 127,
            "text": "        # Only allow submission when there is a job type selected."
          },
          {
            "lineNumber": 129
          },
          {
            "lineNumber": 130,
            "text": "        return job_type is not None"
          },
          {
            "lineNumber": 131
          },
          {
            "lineNumber": 132,
            "text": "    def execute(self, context: bpy.types.Context) -\u003e set[str]:"
          },
          {
            "lineNumber": 133,
            "text": "        filepath, ok = self._presubmit_check(context)"
          },
          {
            "lineNumber": 134,
            "text": "        if not ok:"
          },
          {
            "lineNumber": 135,
            "text": "            return {\"CANCELLED\"}"
          },
          {
            "lineNumber": 136
          },
          {
            "lineNumber": 137,
            "text": "        is_running = self._submit_files(context, filepath)"
          },
          {
            "lineNumber": 138,
            "text": "        if not is_running:"
          },
          {
            "lineNumber": 139,
            "text": "            return {\"CANCELLED\"}"
          },
          {
            "lineNumber": 140
          },
          {
            "lineNumber": 141,
            "text": "        if self.packthread is None:"
          },
          {
            "lineNumber": 142,
            "text": "            # If there is no pack thread running, there isn't much we can do."
          },
          {
            "lineNumber": 143,
            "text": "            self.report({\"ERROR\"}, \"No pack thread running, please report a bug\")"
          },
          {
            "lineNumber": 144,
            "text": "            self._quit(context)"
          },
          {
            "lineNumber": 145,
            "text": "            return {\"CANCELLED\"}"
          },
          {
            "lineNumber": 146
          },
          {
            "lineNumber": 147,
            "text": "        # Keep handling messages from the background thread."
          },
          {
            "lineNumber": 148,
            "text": "        while True:"
          },
          {
            "lineNumber": 149,
            "text": "            # Block for 5 seconds at a time. The exact duration doesn't matter,"
          },
          {
            "lineNumber": 150,
            "text": "            # as this while-loop is blocking the main thread anyway."
          },
          {
            "lineNumber": 151,
            "text": "            msg = self.packthread.poll(timeout=5)"
          },
          {
            "lineNumber": 152,
            "text": "            if not msg:"
          },
          {
            "lineNumber": 153,
            "text": "                # No message received, is fine, just wait for another one."
          },
          {
            "lineNumber": 154,
            "text": "                continue"
          },
          {
            "lineNumber": 155
          },
          {
            "lineNumber": 156,
            "text": "            result = self._on_bat_pack_msg(context, msg)"
          },
          {
            "lineNumber": 157,
            "text": "            if \"RUNNING_MODAL\" not in result:"
          },
          {
            "lineNumber": 158,
            "text": "                break"
          },
          {
            "lineNumber": 159
          },
          {
            "lineNumber": 160,
            "text": "        self._quit(context)"
          },
          {
            "lineNumber": 161,
            "text": "        self.packthread.join(timeout=5)"
          },
          {
            "lineNumber": 162,
            "text": "        return {\"FINISHED\"}"
          },
          {
            "lineNumber": 163
          },
          {
            "lineNumber": 164,
            "text": "    def invoke(self, context: bpy.types.Context, event: bpy.types.Event) -\u003e set[str]:"
          },
          {
            "lineNumber": 165
          },
          {
            "lineNumber": 166,
            "text": "        filepath, ok = self._presubmit_check(context)"
          },
          {
            "lineNumber": 167,
            "text": "        if not ok:"
          },
          {
            "lineNumber": 168,
            "text": "            return {\"CANCELLED\"}"
          },
          {
            "lineNumber": 169
          },
          {
            "lineNumber": 170,
            "text": "        is_running = self._submit_files(context, filepath)"
          },
          {
            "lineNumber": 171,
            "text": "        if not is_running:"
          },
          {
            "lineNumber": 172,
            "text": "            return {\"CANCELLED\"}"
          },
          {
            "lineNumber": 173
          },
          {
            "lineNumber": 174,
            "text": "        context.window_manager.modal_handler_add(self)"
          },
          {
            "lineNumber": 175,
            "text": "        return {\"RUNNING_MODAL\"}"
          },
          {
            "lineNumber": 176
          },
          {
            "lineNumber": 177,
            "text": "    def modal(self, context: bpy.types.Context, event: bpy.types.Event) -\u003e set[str]:"
          },
          {
            "lineNumber": 178,
            "text": "        # This function is called for TIMER events to poll the BAT pack thread."
          },
          {
            "lineNumber": 179,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.37724435329437256
    },
    {
      "codeBlock": {
        "contents": "# Mypy doesn't understand that submodules.pack.Packer exists.\nclass Packer(submodules.pack.Packer):  # type: ignore\n    \"\"\"Creates BAT Packs on a Shaman server.\"\"\"\n\n    def __init__(\n        self,\n        blendfile: Path,\n        project_root: Path,\n        target: str,\n        *,\n        api_client: _ApiClient,\n        checkout_path: str,\n        **kwargs: dict[Any, Any],\n    ) -\u003e None:\n        \"\"\"Constructor\n\n        :param target: mock target root directory to construct project-relative paths.\n        \"\"\"\n        super().__init__(blendfile, project_root, target, **kwargs)\n        self.checkout_path = checkout_path\n        self.api_client = api_client\n        self.shaman_transferrer: Optional[Transferrer] = None\n\n    # Mypy doesn't understand that submodules.transfer.FileTransferer exists.\n    def _create_file_transferer(self) -\u003e submodules.transfer.FileTransferer:  # type: ignore\n        self.shaman_transferrer = Transferrer(\n            self.api_client, self.project, self.checkout_path\n        )\n        return self.shaman_transferrer\n\n    def _make_target_path(self, target: str) -\u003e PurePath:\n        return _root_path()\n\n    @property\n    def output_path(self) -\u003e PurePath:\n        \"\"\"The path of the packed blend file in the target directory.\"\"\"\n        assert self._output_path is not None\n\n        rel_output = self._output_path.relative_to(self._target_path)\n        out_path: PurePath = self.actual_checkout_path / rel_output\n        return out_path\n\n    @property\n    def actual_checkout_path(self) -\u003e PurePosixPath:\n        \"\"\"The actual Shaman checkout path.\n\n        Only valid after packing is complete. Shaman ensures that the checkout\n        is unique, and thus the actual path can be different than the requested\n        one.\n        \"\"\"\n        assert self.shaman_transferrer is not None\n        return PurePosixPath(self.shaman_transferrer.checkout_path)\n\n    def execute(self):\n        try:\n            super().execute()",
        "detailedLines": [
          {
            "lineNumber": 37,
            "text": "# Mypy doesn't understand that submodules.pack.Packer exists."
          },
          {
            "isSignature": true,
            "lineNumber": 38,
            "text": "class Packer(submodules.pack.Packer):  # type: ignore"
          },
          {
            "lineNumber": 39,
            "text": "    \"\"\"Creates BAT Packs on a Shaman server.\"\"\""
          },
          {
            "lineNumber": 40
          },
          {
            "lineNumber": 41,
            "text": "    def __init__("
          },
          {
            "lineNumber": 42,
            "text": "        self,"
          },
          {
            "lineNumber": 43,
            "text": "        blendfile: Path,"
          },
          {
            "lineNumber": 44,
            "text": "        project_root: Path,"
          },
          {
            "lineNumber": 45,
            "text": "        target: str,"
          },
          {
            "lineNumber": 46,
            "text": "        *,"
          },
          {
            "lineNumber": 47,
            "text": "        api_client: _ApiClient,"
          },
          {
            "lineNumber": 48,
            "text": "        checkout_path: str,"
          },
          {
            "lineNumber": 49,
            "text": "        **kwargs: dict[Any, Any],"
          },
          {
            "lineNumber": 50,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 51,
            "text": "        \"\"\"Constructor"
          },
          {
            "lineNumber": 52
          },
          {
            "lineNumber": 53,
            "text": "        :param target: mock target root directory to construct project-relative paths."
          },
          {
            "lineNumber": 54,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 55,
            "text": "        super().__init__(blendfile, project_root, target, **kwargs)"
          },
          {
            "lineNumber": 56,
            "text": "        self.checkout_path = checkout_path"
          },
          {
            "lineNumber": 57,
            "text": "        self.api_client = api_client"
          },
          {
            "lineNumber": 58,
            "text": "        self.shaman_transferrer: Optional[Transferrer] = None"
          },
          {
            "lineNumber": 59
          },
          {
            "lineNumber": 60,
            "text": "    # Mypy doesn't understand that submodules.transfer.FileTransferer exists."
          },
          {
            "lineNumber": 61,
            "text": "    def _create_file_transferer(self) -\u003e submodules.transfer.FileTransferer:  # type: ignore"
          },
          {
            "lineNumber": 62,
            "text": "        self.shaman_transferrer = Transferrer("
          },
          {
            "lineNumber": 63,
            "text": "            self.api_client, self.project, self.checkout_path"
          },
          {
            "lineNumber": 64,
            "text": "        )"
          },
          {
            "lineNumber": 65,
            "text": "        return self.shaman_transferrer"
          },
          {
            "lineNumber": 66
          },
          {
            "lineNumber": 67,
            "text": "    def _make_target_path(self, target: str) -\u003e PurePath:"
          },
          {
            "lineNumber": 68,
            "text": "        return _root_path()"
          },
          {
            "lineNumber": 69
          },
          {
            "lineNumber": 70,
            "text": "    @property"
          },
          {
            "lineNumber": 71,
            "text": "    def output_path(self) -\u003e PurePath:"
          },
          {
            "lineNumber": 72,
            "text": "        \"\"\"The path of the packed blend file in the target directory.\"\"\""
          },
          {
            "lineNumber": 73,
            "text": "        assert self._output_path is not None"
          },
          {
            "lineNumber": 74
          },
          {
            "lineNumber": 75,
            "text": "        rel_output = self._output_path.relative_to(self._target_path)"
          },
          {
            "lineNumber": 76,
            "text": "        out_path: PurePath = self.actual_checkout_path / rel_output"
          },
          {
            "lineNumber": 77,
            "text": "        return out_path"
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "    @property"
          },
          {
            "lineNumber": 80,
            "text": "    def actual_checkout_path(self) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 81,
            "text": "        \"\"\"The actual Shaman checkout path."
          },
          {
            "lineNumber": 82
          },
          {
            "lineNumber": 83,
            "text": "        Only valid after packing is complete. Shaman ensures that the checkout"
          },
          {
            "lineNumber": 84,
            "text": "        is unique, and thus the actual path can be different than the requested"
          },
          {
            "lineNumber": 85,
            "text": "        one."
          },
          {
            "lineNumber": 86,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 87,
            "text": "        assert self.shaman_transferrer is not None"
          },
          {
            "lineNumber": 88,
            "text": "        return PurePosixPath(self.shaman_transferrer.checkout_path)"
          },
          {
            "lineNumber": 89
          },
          {
            "lineNumber": 90,
            "text": "    def execute(self):"
          },
          {
            "lineNumber": 91,
            "text": "        try:"
          },
          {
            "lineNumber": 92,
            "text": "            super().execute()"
          }
        ],
        "range": {
          "endPosition": {
            "line": 95
          },
          "startPosition": {
            "column": 72,
            "line": 33
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.37476134300231934
    },
    {
      "codeBlock": {
        "contents": "log = logging.getLogger(__name__)\n\n\ndef job_for_scene(scene: bpy.types.Scene) -\u003e Optional[_SubmittedJob]:\n    from flamenco.manager.models import SubmittedJob, JobMetadata\n    from flamenco.manager.model.job_status import JobStatus\n\n    propgroup = getattr(scene, \"flamenco_job_settings\", None)\n    assert isinstance(propgroup, JobTypePropertyGroup), \"did not expect %s\" % (\n        type(propgroup)\n    )\n\n    settings = propgroup.as_jobsettings()\n    metadata = JobMetadata()\n\n    priority = getattr(scene, \"flamenco_job_priority\", 50)\n\n    submit_as_paused = getattr(scene, \"flamenco_job_submit_as_paused\", False)\n    if submit_as_paused:\n        initial_status = JobStatus(\"paused\")\n    else:\n        initial_status = JobStatus(\"queued\")\n\n    job: SubmittedJob = SubmittedJob(\n        name=scene.flamenco_job_name,\n        type=propgroup.job_type.name,\n        priority=priority,\n        settings=settings,\n        metadata=metadata,\n        submitter_platform=platform.system().lower(),\n        type_etag=propgroup.job_type.etag,\n        initial_status=initial_status,\n    )\n\n    worker_tag: str = getattr(scene, \"flamenco_worker_tag\", \"\")\n    if worker_tag and worker_tag != \"-\":\n        job.worker_tag = worker_tag\n\n    return job\n\n\ndef set_blend_file(\n    job_type: _AvailableJobType,\n    job: _SubmittedJob,\n    blendfile: Union[str, Path, PurePosixPath],\n) -\u003e None:\n    ",
        "detailedLines": [
          {
            "lineNumber": 31,
            "text": "log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 32
          },
          {
            "lineNumber": 33
          },
          {
            "isSignature": true,
            "lineNumber": 34,
            "text": "def job_for_scene(scene: bpy.types.Scene) -\u003e Optional[_SubmittedJob]:"
          },
          {
            "lineNumber": 35,
            "text": "    from flamenco.manager.models import SubmittedJob, JobMetadata"
          },
          {
            "lineNumber": 36,
            "text": "    from flamenco.manager.model.job_status import JobStatus"
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38,
            "text": "    propgroup = getattr(scene, \"flamenco_job_settings\", None)"
          },
          {
            "lineNumber": 39,
            "text": "    assert isinstance(propgroup, JobTypePropertyGroup), \"did not expect %s\" % ("
          },
          {
            "lineNumber": 40,
            "text": "        type(propgroup)"
          },
          {
            "lineNumber": 41,
            "text": "    )"
          },
          {
            "lineNumber": 42
          },
          {
            "lineNumber": 43,
            "text": "    settings = propgroup.as_jobsettings()"
          },
          {
            "lineNumber": 44,
            "text": "    metadata = JobMetadata()"
          },
          {
            "lineNumber": 45
          },
          {
            "lineNumber": 46,
            "text": "    priority = getattr(scene, \"flamenco_job_priority\", 50)"
          },
          {
            "lineNumber": 47
          },
          {
            "lineNumber": 48,
            "text": "    submit_as_paused = getattr(scene, \"flamenco_job_submit_as_paused\", False)"
          },
          {
            "lineNumber": 49,
            "text": "    if submit_as_paused:"
          },
          {
            "lineNumber": 50,
            "text": "        initial_status = JobStatus(\"paused\")"
          },
          {
            "lineNumber": 51,
            "text": "    else:"
          },
          {
            "lineNumber": 52,
            "text": "        initial_status = JobStatus(\"queued\")"
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54,
            "text": "    job: SubmittedJob = SubmittedJob("
          },
          {
            "lineNumber": 55,
            "text": "        name=scene.flamenco_job_name,"
          },
          {
            "lineNumber": 56,
            "text": "        type=propgroup.job_type.name,"
          },
          {
            "lineNumber": 57,
            "text": "        priority=priority,"
          },
          {
            "lineNumber": 58,
            "text": "        settings=settings,"
          },
          {
            "lineNumber": 59,
            "text": "        metadata=metadata,"
          },
          {
            "lineNumber": 60,
            "text": "        submitter_platform=platform.system().lower(),"
          },
          {
            "lineNumber": 61,
            "text": "        type_etag=propgroup.job_type.etag,"
          },
          {
            "lineNumber": 62,
            "text": "        initial_status=initial_status,"
          },
          {
            "lineNumber": 63,
            "text": "    )"
          },
          {
            "lineNumber": 64
          },
          {
            "lineNumber": 65,
            "text": "    worker_tag: str = getattr(scene, \"flamenco_worker_tag\", \"\")"
          },
          {
            "lineNumber": 66,
            "text": "    if worker_tag and worker_tag != \"-\":"
          },
          {
            "lineNumber": 67,
            "text": "        job.worker_tag = worker_tag"
          },
          {
            "lineNumber": 68
          },
          {
            "isSignature": true,
            "lineNumber": 69,
            "text": "    return job"
          },
          {
            "lineNumber": 70
          },
          {
            "lineNumber": 71
          },
          {
            "isSignature": true,
            "lineNumber": 72,
            "text": "def set_blend_file("
          },
          {
            "isSignature": true,
            "lineNumber": 73,
            "text": "    job_type: _AvailableJobType,"
          },
          {
            "isSignature": true,
            "lineNumber": 74,
            "text": "    job: _SubmittedJob,"
          },
          {
            "isSignature": true,
            "lineNumber": 75,
            "text": "    blendfile: Union[str, Path, PurePosixPath],"
          },
          {
            "isSignature": true,
            "lineNumber": 76,
            "text": ") -\u003e None:"
          },
          {
            "lineNumber": 77,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 92
          },
          "startPosition": {
            "column": 35,
            "line": 28
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.3747037947177887
    },
    {
      "codeBlock": {
        "contents": "def submit_job(job: _SubmittedJob, api_client: _ApiClient) -\u003e _Job:\n    \"\"\"Send the given job to Flamenco Manager.\"\"\"\n    from flamenco.manager import ApiClient\n    from flamenco.manager.api import jobs_api\n    from flamenco.manager.models import SubmittedJob, Job\n\n    assert isinstance(job, SubmittedJob), \"got %s\" % type(job)\n    assert isinstance(api_client, ApiClient), \"got %s\" % type(api_client)\n\n    job_api_instance = jobs_api.JobsApi(api_client)\n    response: Job = job_api_instance.submit_job(job)\n    print(\"Job submitted: %s (%s)\" % (response.name, response.id))\n\n    return response\n\n\ndef submit_job_check(job: _SubmittedJob, api_client: _ApiClient) -\u003e None:\n    \"\"\"Check the given job at Flamenco Manager to see if it is acceptable.\"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 105,
            "text": "def submit_job(job: _SubmittedJob, api_client: _ApiClient) -\u003e _Job:"
          },
          {
            "lineNumber": 106,
            "text": "    \"\"\"Send the given job to Flamenco Manager.\"\"\""
          },
          {
            "lineNumber": 107,
            "text": "    from flamenco.manager import ApiClient"
          },
          {
            "lineNumber": 108,
            "text": "    from flamenco.manager.api import jobs_api"
          },
          {
            "lineNumber": 109,
            "text": "    from flamenco.manager.models import SubmittedJob, Job"
          },
          {
            "lineNumber": 110
          },
          {
            "lineNumber": 111,
            "text": "    assert isinstance(job, SubmittedJob), \"got %s\" % type(job)"
          },
          {
            "lineNumber": 112,
            "text": "    assert isinstance(api_client, ApiClient), \"got %s\" % type(api_client)"
          },
          {
            "lineNumber": 113
          },
          {
            "lineNumber": 114,
            "text": "    job_api_instance = jobs_api.JobsApi(api_client)"
          },
          {
            "lineNumber": 115,
            "text": "    response: Job = job_api_instance.submit_job(job)"
          },
          {
            "lineNumber": 116,
            "text": "    print(\"Job submitted: %s (%s)\" % (response.name, response.id))"
          },
          {
            "lineNumber": 117
          },
          {
            "isSignature": true,
            "lineNumber": 118,
            "text": "    return response"
          },
          {
            "lineNumber": 119
          },
          {
            "lineNumber": 120
          },
          {
            "isSignature": true,
            "lineNumber": 121,
            "text": "def submit_job_check(job: _SubmittedJob, api_client: _ApiClient) -\u003e None:"
          },
          {
            "lineNumber": 122,
            "text": "    \"\"\"Check the given job at Flamenco Manager to see if it is acceptable.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 130
          },
          "startPosition": {
            "column": 59,
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.36784154176712036
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:\n\n\n        return True\n\n    def _submit_job(self, context: bpy.types.Context) -\u003e None:\n        \"\"\"Use the Flamenco API to submit the new Job.\"\"\"\n        assert self.job is not None\n        assert self.blendfile_on_farm is not None\n\n        from flamenco.manager import ApiException\n\n        if not self._prepare_job_for_submission(context):\n            return\n\n        api_client = self.get_api_client(context)\n        try:\n            submitted_job = job_submission.submit_job(self.job, api_client)\n        except MaxRetryError as ex:\n            self.report({\"ERROR\"}, \"Unable to reach Flamenco Manager\")\n            return\n        except HTTPError as ex:\n            self.report({\"ERROR\"}, \"Error communicating with Flamenco Manager: %s\" % ex)\n            return\n        except ApiException as ex:\n            if ex.status == 412:\n                self.report(\n                    {\"ERROR\"},\n                    \"Cached job type is old. Refresh the job types and submit again, please\",\n                )\n                return\n            if ex.status == 400:\n                error = parse_api_error(api_client, ex)\n                self.report({\"ERROR\"}, error.message)\n                return\n            self.report({\"ERROR\"}, f\"Could not submit job: {ex.reason}\")\n            return\n\n        self.report({\"INFO\"}, \"Job %s submitted\" % submitted_job.name)\n\n    def _check_job(self, context: bpy.types.Context) -\u003e bool:\n        \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 542,
            "text": "def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 566
          },
          {
            "lineNumber": 567
          },
          {
            "lineNumber": 568,
            "text": "        return True"
          },
          {
            "lineNumber": 569
          },
          {
            "lineNumber": 570,
            "text": "    def _submit_job(self, context: bpy.types.Context) -\u003e None:"
          },
          {
            "lineNumber": 571,
            "text": "        \"\"\"Use the Flamenco API to submit the new Job.\"\"\""
          },
          {
            "lineNumber": 572,
            "text": "        assert self.job is not None"
          },
          {
            "lineNumber": 573,
            "text": "        assert self.blendfile_on_farm is not None"
          },
          {
            "lineNumber": 574
          },
          {
            "lineNumber": 575,
            "text": "        from flamenco.manager import ApiException"
          },
          {
            "lineNumber": 576
          },
          {
            "lineNumber": 577,
            "text": "        if not self._prepare_job_for_submission(context):"
          },
          {
            "lineNumber": 578,
            "text": "            return"
          },
          {
            "lineNumber": 579
          },
          {
            "lineNumber": 580,
            "text": "        api_client = self.get_api_client(context)"
          },
          {
            "lineNumber": 581,
            "text": "        try:"
          },
          {
            "lineNumber": 582,
            "text": "            submitted_job = job_submission.submit_job(self.job, api_client)"
          },
          {
            "lineNumber": 583,
            "text": "        except MaxRetryError as ex:"
          },
          {
            "lineNumber": 584,
            "text": "            self.report({\"ERROR\"}, \"Unable to reach Flamenco Manager\")"
          },
          {
            "lineNumber": 585,
            "text": "            return"
          },
          {
            "lineNumber": 586,
            "text": "        except HTTPError as ex:"
          },
          {
            "lineNumber": 587,
            "text": "            self.report({\"ERROR\"}, \"Error communicating with Flamenco Manager: %s\" % ex)"
          },
          {
            "lineNumber": 588,
            "text": "            return"
          },
          {
            "lineNumber": 589,
            "text": "        except ApiException as ex:"
          },
          {
            "lineNumber": 590,
            "text": "            if ex.status == 412:"
          },
          {
            "lineNumber": 591,
            "text": "                self.report("
          },
          {
            "lineNumber": 592,
            "text": "                    {\"ERROR\"},"
          },
          {
            "lineNumber": 593,
            "text": "                    \"Cached job type is old. Refresh the job types and submit again, please\","
          },
          {
            "lineNumber": 594,
            "text": "                )"
          },
          {
            "lineNumber": 595,
            "text": "                return"
          },
          {
            "lineNumber": 596,
            "text": "            if ex.status == 400:"
          },
          {
            "lineNumber": 597,
            "text": "                error = parse_api_error(api_client, ex)"
          },
          {
            "lineNumber": 598,
            "text": "                self.report({\"ERROR\"}, error.message)"
          },
          {
            "lineNumber": 599,
            "text": "                return"
          },
          {
            "lineNumber": 600,
            "text": "            self.report({\"ERROR\"}, f\"Could not submit job: {ex.reason}\")"
          },
          {
            "lineNumber": 601,
            "text": "            return"
          },
          {
            "lineNumber": 602
          },
          {
            "lineNumber": 603,
            "text": "        self.report({\"INFO\"}, \"Job %s submitted\" % submitted_job.name)"
          },
          {
            "lineNumber": 604
          },
          {
            "lineNumber": 605,
            "text": "    def _check_job(self, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 606,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.36433538794517517
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n# \u003cpep8 compliant\u003e\n\nfrom pathlib import Path\nfrom typing import Callable\nimport dataclasses\n\nfrom .bat.submodules import bpathlib\n\n\ndef for_blendfile(blendfile: Path, strategy: str) -\u003e Path:\n    \"\"\"Return what is considered to be the project directory containing the given file.\n\n    If none can be found, the directory containing the current blend file is returned.\n    If the current blend file has no path (because it was not saved), a ValueError is raised.\n\n    :param blendfile: the path of the blend file for which to find the project.\n    :param strategy: the name of the finder to use, see &#96;finders&#96;.\n    \"\"\"\n    if blendfile.is_dir():\n        msg = f\"{blendfile} is not a blend file, cannot find project directory\"\n        raise ValueError(msg)\n\n    try:\n        finder_info = finders[strategy]\n    except KeyError:\n        msg = f\"Unknown strategy {strategy!r}, cannot find project directory\"\n        raise ValueError(msg) from None\n\n    return finder_info.finder(blendfile)\n\n\ndef _finder_blender_project(blendfile: Path) -\u003e Path:\n    return _search_path_marker(blendfile, \".blender_project\")",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "# \u003cpep8 compliant\u003e"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "from pathlib import Path"
          },
          {
            "lineNumber": 5,
            "text": "from typing import Callable"
          },
          {
            "lineNumber": 6,
            "text": "import dataclasses"
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "from .bat.submodules import bpathlib"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "isSignature": true,
            "lineNumber": 11,
            "text": "def for_blendfile(blendfile: Path, strategy: str) -\u003e Path:"
          },
          {
            "lineNumber": 12,
            "text": "    \"\"\"Return what is considered to be the project directory containing the given file."
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "    If none can be found, the directory containing the current blend file is returned."
          },
          {
            "lineNumber": 15,
            "text": "    If the current blend file has no path (because it was not saved), a ValueError is raised."
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "    :param blendfile: the path of the blend file for which to find the project."
          },
          {
            "lineNumber": 18,
            "text": "    :param strategy: the name of the finder to use, see &#96;finders&#96;."
          },
          {
            "lineNumber": 19,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 20,
            "text": "    if blendfile.is_dir():"
          },
          {
            "lineNumber": 21,
            "text": "        msg = f\"{blendfile} is not a blend file, cannot find project directory\""
          },
          {
            "lineNumber": 22,
            "text": "        raise ValueError(msg)"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "    try:"
          },
          {
            "lineNumber": 25,
            "text": "        finder_info = finders[strategy]"
          },
          {
            "lineNumber": 26,
            "text": "    except KeyError:"
          },
          {
            "lineNumber": 27,
            "text": "        msg = f\"Unknown strategy {strategy!r}, cannot find project directory\""
          },
          {
            "lineNumber": 28,
            "text": "        raise ValueError(msg) from None"
          },
          {
            "lineNumber": 29
          },
          {
            "isSignature": true,
            "lineNumber": 30,
            "text": "    return finder_info.finder(blendfile)"
          },
          {
            "lineNumber": 31
          },
          {
            "lineNumber": 32
          },
          {
            "isSignature": true,
            "lineNumber": 33,
            "text": "def _finder_blender_project(blendfile: Path) -\u003e Path:"
          },
          {
            "isSignature": true,
            "lineNumber": 34,
            "text": "    return _search_path_marker(blendfile, \".blender_project\")"
          }
        ],
        "range": {
          "endPosition": {
            "column": 61,
            "line": 33
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\projects.py",
        "signatures": {}
      },
      "score": 0.36220911145210266
    },
    {
      "codeBlock": {
        "contents": "def _default_job_name() -\u003e str:\n    if not bpy.data.filepath:\n        return \"\"\n    return Path(bpy.data.filepath).stem\n\n\n@bpy.app.handlers.persistent\ndef _set_flamenco_job_name(a, b):\n    scene = bpy.context.scene",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 67,
            "text": "def _default_job_name() -\u003e str:"
          },
          {
            "lineNumber": 68,
            "text": "    if not bpy.data.filepath:"
          },
          {
            "lineNumber": 69,
            "text": "        return \"\""
          },
          {
            "isSignature": true,
            "lineNumber": 70,
            "text": "    return Path(bpy.data.filepath).stem"
          },
          {
            "lineNumber": 71
          },
          {
            "lineNumber": 72
          },
          {
            "lineNumber": 73,
            "text": "@bpy.app.handlers.persistent"
          },
          {
            "isSignature": true,
            "lineNumber": 74,
            "text": "def _set_flamenco_job_name(a, b):"
          },
          {
            "lineNumber": 75,
            "text": "    scene = bpy.context.scene"
          }
        ],
        "range": {
          "endPosition": {
            "line": 78
          },
          "startPosition": {
            "column": 29,
            "line": 63
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\__init__.py",
        "signatures": {}
      },
      "score": 0.3547935485839844
    },
    {
      "codeBlock": {
        "contents": "class PackThread(threading.Thread):\n    queue: queue.SimpleQueue[Message]\n\n    # MyPy doesn't understand the way BAT subpackages are imported.\n    def __init__(self, packer: submodules.pack.Packer) -\u003e None:  # type: ignore\n        # Quitting Blender should abort the transfer (instead of hanging until\n        # the transfer is done), hence daemon=True.\n        super().__init__(daemon=True, name=\"PackThread\")\n\n        self.queue = queue.SimpleQueue()\n\n        self.packer = packer\n        self.packer.progress_cb = BatProgress(queue=self.queue)\n\n    def run(self) -\u003e None:\n        global _running_packthread\n\n        try:\n            self._run()\n        except BaseException as ex:\n            self._set_bat_status(\"ABORTED\")\n            log.exception(\"Error packing with BAT: %s\", ex)\n            self.queue.put(MsgException(ex=ex))\n        finally:\n            with _packer_lock:\n                _running_packthread = None\n\n    def _run(self) -\u003e None:\n        with self.packer:\n            log.debug(\"awaiting strategise\")\n            self._set_bat_status(\"INVESTIGATING\")\n            self.packer.strategise()\n\n            log.debug(\"awaiting execute\")\n            self._set_bat_status(\"TRANSFERRING\")\n            self.packer.execute()\n\n            log.debug(\"done\")\n            self._set_bat_status(\"DONE\")\n\n            msg = MsgDone(\n                self.packer.output_path,\n                self.packer.missing_files,\n                getattr(self.packer, \"actual_checkout_path\", None),\n            )\n            self.queue.put(msg)\n\n    def _set_bat_status(self, status: str) -\u003e None:\n        self.queue.put(MsgSetWMAttribute(\"flamenco_bat_status\", status))\n\n    def poll(self, timeout: Optional[int] = None) -\u003e Optional[Message]:\n        \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 130,
            "text": "class PackThread(threading.Thread):"
          },
          {
            "lineNumber": 131,
            "text": "    queue: queue.SimpleQueue[Message]"
          },
          {
            "lineNumber": 132
          },
          {
            "lineNumber": 133,
            "text": "    # MyPy doesn't understand the way BAT subpackages are imported."
          },
          {
            "lineNumber": 134,
            "text": "    def __init__(self, packer: submodules.pack.Packer) -\u003e None:  # type: ignore"
          },
          {
            "lineNumber": 135,
            "text": "        # Quitting Blender should abort the transfer (instead of hanging until"
          },
          {
            "lineNumber": 136,
            "text": "        # the transfer is done), hence daemon=True."
          },
          {
            "lineNumber": 137,
            "text": "        super().__init__(daemon=True, name=\"PackThread\")"
          },
          {
            "lineNumber": 138
          },
          {
            "lineNumber": 139,
            "text": "        self.queue = queue.SimpleQueue()"
          },
          {
            "lineNumber": 140
          },
          {
            "lineNumber": 141,
            "text": "        self.packer = packer"
          },
          {
            "lineNumber": 142,
            "text": "        self.packer.progress_cb = BatProgress(queue=self.queue)"
          },
          {
            "lineNumber": 143
          },
          {
            "lineNumber": 144,
            "text": "    def run(self) -\u003e None:"
          },
          {
            "lineNumber": 145,
            "text": "        global _running_packthread"
          },
          {
            "lineNumber": 146
          },
          {
            "lineNumber": 147,
            "text": "        try:"
          },
          {
            "lineNumber": 148,
            "text": "            self._run()"
          },
          {
            "lineNumber": 149,
            "text": "        except BaseException as ex:"
          },
          {
            "lineNumber": 150,
            "text": "            self._set_bat_status(\"ABORTED\")"
          },
          {
            "lineNumber": 151,
            "text": "            log.exception(\"Error packing with BAT: %s\", ex)"
          },
          {
            "lineNumber": 152,
            "text": "            self.queue.put(MsgException(ex=ex))"
          },
          {
            "lineNumber": 153,
            "text": "        finally:"
          },
          {
            "lineNumber": 154,
            "text": "            with _packer_lock:"
          },
          {
            "lineNumber": 155,
            "text": "                _running_packthread = None"
          },
          {
            "lineNumber": 156
          },
          {
            "lineNumber": 157,
            "text": "    def _run(self) -\u003e None:"
          },
          {
            "lineNumber": 158,
            "text": "        with self.packer:"
          },
          {
            "lineNumber": 159,
            "text": "            log.debug(\"awaiting strategise\")"
          },
          {
            "lineNumber": 160,
            "text": "            self._set_bat_status(\"INVESTIGATING\")"
          },
          {
            "lineNumber": 161,
            "text": "            self.packer.strategise()"
          },
          {
            "lineNumber": 162
          },
          {
            "lineNumber": 163,
            "text": "            log.debug(\"awaiting execute\")"
          },
          {
            "lineNumber": 164,
            "text": "            self._set_bat_status(\"TRANSFERRING\")"
          },
          {
            "lineNumber": 165,
            "text": "            self.packer.execute()"
          },
          {
            "lineNumber": 166
          },
          {
            "lineNumber": 167,
            "text": "            log.debug(\"done\")"
          },
          {
            "lineNumber": 168,
            "text": "            self._set_bat_status(\"DONE\")"
          },
          {
            "lineNumber": 169
          },
          {
            "lineNumber": 170,
            "text": "            msg = MsgDone("
          },
          {
            "lineNumber": 171,
            "text": "                self.packer.output_path,"
          },
          {
            "lineNumber": 172,
            "text": "                self.packer.missing_files,"
          },
          {
            "lineNumber": 173,
            "text": "                getattr(self.packer, \"actual_checkout_path\", None),"
          },
          {
            "lineNumber": 174,
            "text": "            )"
          },
          {
            "lineNumber": 175,
            "text": "            self.queue.put(msg)"
          },
          {
            "lineNumber": 176
          },
          {
            "lineNumber": 177,
            "text": "    def _set_bat_status(self, status: str) -\u003e None:"
          },
          {
            "lineNumber": 178,
            "text": "        self.queue.put(MsgSetWMAttribute(\"flamenco_bat_status\", status))"
          },
          {
            "lineNumber": 179
          },
          {
            "lineNumber": 180,
            "text": "    def poll(self, timeout: Optional[int] = None) -\u003e Optional[Message]:"
          },
          {
            "lineNumber": 181,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 193
          },
          "startPosition": {
            "column": 32,
            "line": 126
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.3373648524284363
    },
    {
      "codeBlock": {
        "contents": "class ApiClient(object):\n\n\n    def files_parameters(self, files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None):\n        \"\"\"Builds form parameters.\n\n        :param files: None or a dict with key=param_name and\n            value is a list of open file objects\n        :return: List of tuples of form parameters with file data\n        \"\"\"\n        if files is None:\n            return []\n\n        params = []\n        for param_name, file_instances in files.items():\n            if file_instances is None:\n                # if the file field is nullable, skip None values\n                continue\n            for file_instance in file_instances:\n                if file_instance is None:\n                    # if the file field is nullable, skip None values\n                    continue\n                if file_instance.closed is True:\n                    raise ApiValueError(\n                        \"Cannot read a closed file. The passed in file_type \"\n                        \"for %s must be open.\" % param_name\n                    )\n                filename = os.path.basename(file_instance.name)\n                filedata = self.get_file_data_and_close_file(file_instance)\n                mimetype = (mimetypes.guess_type(filename)[0] or\n                            'application/octet-stream')\n                params.append(\n                    tuple([param_name, tuple([filename, filedata, mimetype])]))\n\n        return params\n\n    def select_header_accept(self, accepts):\n        \"\"\"Returns &#96;Accept&#96; based on an array of accepts provided.\n\n        :param accepts: List of headers.\n        :return: Accept (e.g. application/json).\n        \"\"\"\n        if not accepts:\n            return\n\n        accepts = [x.lower() for x in accepts]\n\n        if 'application/json' in accepts:\n            return 'application/json'\n        else:\n            return ', '.join(accepts)\n\n    def select_header_content_type(self, content_types, method=None, body=None):\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 42,
            "text": "class ApiClient(object):"
          },
          {
            "lineNumber": 525
          },
          {
            "lineNumber": 526
          },
          {
            "lineNumber": 527,
            "text": "    def files_parameters(self, files: typing.Optional[typing.Dict[str, typing.List[io.IOBase]]] = None):"
          },
          {
            "lineNumber": 528,
            "text": "        \"\"\"Builds form parameters."
          },
          {
            "lineNumber": 529
          },
          {
            "lineNumber": 530,
            "text": "        :param files: None or a dict with key=param_name and"
          },
          {
            "lineNumber": 531,
            "text": "            value is a list of open file objects"
          },
          {
            "lineNumber": 532,
            "text": "        :return: List of tuples of form parameters with file data"
          },
          {
            "lineNumber": 533,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 534,
            "text": "        if files is None:"
          },
          {
            "lineNumber": 535,
            "text": "            return []"
          },
          {
            "lineNumber": 536
          },
          {
            "lineNumber": 537,
            "text": "        params = []"
          },
          {
            "lineNumber": 538,
            "text": "        for param_name, file_instances in files.items():"
          },
          {
            "lineNumber": 539,
            "text": "            if file_instances is None:"
          },
          {
            "lineNumber": 540,
            "text": "                # if the file field is nullable, skip None values"
          },
          {
            "lineNumber": 541,
            "text": "                continue"
          },
          {
            "lineNumber": 542,
            "text": "            for file_instance in file_instances:"
          },
          {
            "lineNumber": 543,
            "text": "                if file_instance is None:"
          },
          {
            "lineNumber": 544,
            "text": "                    # if the file field is nullable, skip None values"
          },
          {
            "lineNumber": 545,
            "text": "                    continue"
          },
          {
            "lineNumber": 546,
            "text": "                if file_instance.closed is True:"
          },
          {
            "lineNumber": 547,
            "text": "                    raise ApiValueError("
          },
          {
            "lineNumber": 548,
            "text": "                        \"Cannot read a closed file. The passed in file_type \""
          },
          {
            "lineNumber": 549,
            "text": "                        \"for %s must be open.\" % param_name"
          },
          {
            "lineNumber": 550,
            "text": "                    )"
          },
          {
            "lineNumber": 551,
            "text": "                filename = os.path.basename(file_instance.name)"
          },
          {
            "lineNumber": 552,
            "text": "                filedata = self.get_file_data_and_close_file(file_instance)"
          },
          {
            "lineNumber": 553,
            "text": "                mimetype = (mimetypes.guess_type(filename)[0] or"
          },
          {
            "lineNumber": 554,
            "text": "                            'application/octet-stream')"
          },
          {
            "lineNumber": 555,
            "text": "                params.append("
          },
          {
            "lineNumber": 556,
            "text": "                    tuple([param_name, tuple([filename, filedata, mimetype])]))"
          },
          {
            "lineNumber": 557
          },
          {
            "lineNumber": 558,
            "text": "        return params"
          },
          {
            "lineNumber": 559
          },
          {
            "lineNumber": 560,
            "text": "    def select_header_accept(self, accepts):"
          },
          {
            "lineNumber": 561,
            "text": "        \"\"\"Returns &#96;Accept&#96; based on an array of accepts provided."
          },
          {
            "lineNumber": 562
          },
          {
            "lineNumber": 563,
            "text": "        :param accepts: List of headers."
          },
          {
            "lineNumber": 564,
            "text": "        :return: Accept (e.g. application/json)."
          },
          {
            "lineNumber": 565,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 566,
            "text": "        if not accepts:"
          },
          {
            "lineNumber": 567,
            "text": "            return"
          },
          {
            "lineNumber": 568
          },
          {
            "lineNumber": 569,
            "text": "        accepts = [x.lower() for x in accepts]"
          },
          {
            "lineNumber": 570
          },
          {
            "lineNumber": 571,
            "text": "        if 'application/json' in accepts:"
          },
          {
            "lineNumber": 572,
            "text": "            return 'application/json'"
          },
          {
            "lineNumber": 573,
            "text": "        else:"
          },
          {
            "lineNumber": 574,
            "text": "            return ', '.join(accepts)"
          },
          {
            "lineNumber": 575
          },
          {
            "lineNumber": 576,
            "text": "    def select_header_content_type(self, content_types, method=None, body=None):"
          },
          {
            "lineNumber": 577,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 626
          },
          "startPosition": {
            "line": 41
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\api_client.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 43
              },
              "startPosition": {
                "column": 1,
                "line": 42
              }
            }
          ]
        }
      },
      "score": 0.33699536323547363
    }
  ]
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full"><details>
<summary>Tool use: **semantic_search_full**</summary>

</details></tool-use>

_**Agent (default)**_

Reviewing relevant files to understand the current implementation:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\bat\interface.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
<details>
<summary>Tool use: **ripgrep_raw_search**</summary>


Parameters:

```json
{
  "caseInsensitive": false,
  "path": "e:\\flamenco\\addon",
  "pattern": "relative_only"
}
```
Additional data:

```json
{
  "isPruned": true,
  "outputMode": "content",
  "path": "e:\\flamenco\\addon",
  "pattern": "relative_only",
  "topFiles": [
    {
      "matchCount": 2,
      "uri": "addon/flamenco/bat/interface.py"
    },
    {
      "matchCount": 2,
      "uri": "addon/flamenco/operators.py"
    }
  ],
  "totalFiles": 2,
  "totalMatches": 4
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search"><details>
<summary>Tool use: **ripgrep_raw_search**</summary>

</details></tool-use>

_**Agent (default)**_

Checking the shaman method as well:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full">
<details>
<summary>Tool use: **semantic_search_full**</summary>


Parameters:

```json
{
  "explanation": "",
  "includePattern": "addon",
  "query": "How does BAT packer handle relative_only parameter and convert paths to absolute?",
  "topK": 10
}
```
Result:

```json
{
  "codeResults": [
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:\n\n\n        return True\n\n    def _bat_pack_filesystem(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e PurePosixPath:\n        \"\"\"Use BAT to store the pack on the filesystem.\n\n        :return: the path of the blend file, for use in the job definition.\n        \"\"\"\n        from .bat import interface as bat_interface\n\n        # Get project path from addon preferences.\n        prefs = preferences.get(context)\n        project_path: Path = prefs.project_root()\n        project_path = bpathlib.make_absolute(Path(bpy.path.abspath(str(project_path))))\n\n        if not project_path.exists():\n            self.report({\"ERROR\"}, \"Project path %s does not exist\" % project_path)\n            raise FileNotFoundError()\n\n        # Determine where the blend file will be stored.\n        manager = self._manager_info(context)\n        if not manager:\n            raise FileNotFoundError(\"Manager info not known\")\n        unique_dir = \"%s-%s\" % (\n            datetime.datetime.now().isoformat(\"-\").replace(\":\", \"\"),\n            self.job_name,\n        )\n        pack_target_dir = Path(manager.shared_storage.location) / unique_dir\n\n        # TODO: this should take the blendfile location relative to the project path into account.\n        pack_target_file = pack_target_dir / blendfile.name\n        self.log.info(\"Will store blend file at %s\", pack_target_file)\n\n        self.packthread = bat_interface.copy(\n            base_blendfile=blendfile,\n            project=project_path,\n            target=str(pack_target_dir),\n            exclusion_filter=\"\",  # TODO: get from GUI.\n            relative_only=True,  # TODO: get from GUI.\n        )\n\n        return PurePosixPath(pack_target_file.as_posix())\n\n    def _shaman_checkout_path(self) -\u003e PurePosixPath:\n        \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 366,
            "text": "def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 404
          },
          {
            "lineNumber": 405
          },
          {
            "lineNumber": 406,
            "text": "        return True"
          },
          {
            "lineNumber": 407
          },
          {
            "lineNumber": 408,
            "text": "    def _bat_pack_filesystem("
          },
          {
            "lineNumber": 409,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 410,
            "text": "    ) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 411,
            "text": "        \"\"\"Use BAT to store the pack on the filesystem."
          },
          {
            "lineNumber": 412
          },
          {
            "lineNumber": 413,
            "text": "        :return: the path of the blend file, for use in the job definition."
          },
          {
            "lineNumber": 414,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 415,
            "text": "        from .bat import interface as bat_interface"
          },
          {
            "lineNumber": 416
          },
          {
            "lineNumber": 417,
            "text": "        # Get project path from addon preferences."
          },
          {
            "lineNumber": 418,
            "text": "        prefs = preferences.get(context)"
          },
          {
            "lineNumber": 419,
            "text": "        project_path: Path = prefs.project_root()"
          },
          {
            "lineNumber": 420,
            "text": "        project_path = bpathlib.make_absolute(Path(bpy.path.abspath(str(project_path))))"
          },
          {
            "lineNumber": 421
          },
          {
            "lineNumber": 422,
            "text": "        if not project_path.exists():"
          },
          {
            "lineNumber": 423,
            "text": "            self.report({\"ERROR\"}, \"Project path %s does not exist\" % project_path)"
          },
          {
            "lineNumber": 424,
            "text": "            raise FileNotFoundError()"
          },
          {
            "lineNumber": 425
          },
          {
            "lineNumber": 426,
            "text": "        # Determine where the blend file will be stored."
          },
          {
            "lineNumber": 427,
            "text": "        manager = self._manager_info(context)"
          },
          {
            "lineNumber": 428,
            "text": "        if not manager:"
          },
          {
            "lineNumber": 429,
            "text": "            raise FileNotFoundError(\"Manager info not known\")"
          },
          {
            "lineNumber": 430,
            "text": "        unique_dir = \"%s-%s\" % ("
          },
          {
            "lineNumber": 431,
            "text": "            datetime.datetime.now().isoformat(\"-\").replace(\":\", \"\"),"
          },
          {
            "lineNumber": 432,
            "text": "            self.job_name,"
          },
          {
            "lineNumber": 433,
            "text": "        )"
          },
          {
            "lineNumber": 434,
            "text": "        pack_target_dir = Path(manager.shared_storage.location) / unique_dir"
          },
          {
            "lineNumber": 435
          },
          {
            "lineNumber": 436,
            "text": "        # TODO: this should take the blendfile location relative to the project path into account."
          },
          {
            "lineNumber": 437,
            "text": "        pack_target_file = pack_target_dir / blendfile.name"
          },
          {
            "lineNumber": 438,
            "text": "        self.log.info(\"Will store blend file at %s\", pack_target_file)"
          },
          {
            "lineNumber": 439
          },
          {
            "lineNumber": 440,
            "text": "        self.packthread = bat_interface.copy("
          },
          {
            "lineNumber": 441,
            "text": "            base_blendfile=blendfile,"
          },
          {
            "lineNumber": 442,
            "text": "            project=project_path,"
          },
          {
            "lineNumber": 443,
            "text": "            target=str(pack_target_dir),"
          },
          {
            "lineNumber": 444,
            "text": "            exclusion_filter=\"\",  # TODO: get from GUI."
          },
          {
            "lineNumber": 445,
            "text": "            relative_only=True,  # TODO: get from GUI."
          },
          {
            "lineNumber": 446,
            "text": "        )"
          },
          {
            "lineNumber": 447
          },
          {
            "lineNumber": 448,
            "text": "        return PurePosixPath(pack_target_file.as_posix())"
          },
          {
            "lineNumber": 449
          },
          {
            "lineNumber": 450,
            "text": "    def _shaman_checkout_path(self) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 451,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.4294227361679077
    },
    {
      "codeBlock": {
        "contents": "_running_packthread: typing.Optional[PackThread] = None\n_packer_lock = threading.RLock()\n\n\ndef copy(  # type: ignore\n    base_blendfile: Path,\n    project: Path,\n    target: str,\n    exclusion_filter: str,\n    *,\n    relative_only: bool,\n    packer_class=submodules.pack.Packer,\n    packer_kwargs: Optional[dict[Any, Any]] = None,\n) -\u003e PackThread:\n    \"\"\"Use BAT to copy the given file and dependencies to the target location.\n\n    Runs BAT in a separate thread, and returns early. Use poll() to get updates\n    \u0026 the final result.\n    \"\"\"\n    global _running_packthread\n\n    with _packer_lock:\n        if _running_packthread is not None:\n            raise RuntimeError(\"other packing operation already in progress\")\n\n    # Due to issues with library overrides and unsynced pointers, it's quite\n    # common for the Blender Animation Studio to get crashes of BAT. To avoid\n    # these, Strict Pointer Mode is disabled.\n    submodules.blendfile.set_strict_pointer_mode(False)\n\n    log.info(\"BAT pack parameters:\")\n    log.info(\"base_blendfile = %r\", base_blendfile)\n    log.info(\"project        = %r\", project)\n    log.info(\"target         = %r\", target)\n\n    if packer_kwargs is None:\n        packer_kwargs = {}\n    packer = packer_class(\n        base_blendfile,\n        project,\n        target,\n        compress=True,\n        relative_only=relative_only,\n        **packer_kwargs,\n    )\n    if exclusion_filter:\n        filter_parts = exclusion_filter.strip().split(\" \")\n        packer.exclude(*filter_parts)\n\n    packthread = PackThread(packer=packer)\n    with _packer_lock:\n        _running_packthread = packthread\n\n    packthread.start()\n    return packthread\n\n\ndef abort() -\u003e None:\n    \"\"\"Abort a running copy() call.\n\n    No-op when there is no running copy(). Can be called from any thread.\n    ",
        "detailedLines": [
          {
            "lineNumber": 197,
            "text": "_running_packthread: typing.Optional[PackThread] = None"
          },
          {
            "lineNumber": 198,
            "text": "_packer_lock = threading.RLock()"
          },
          {
            "lineNumber": 199
          },
          {
            "lineNumber": 200
          },
          {
            "isSignature": true,
            "lineNumber": 201,
            "text": "def copy(  # type: ignore"
          },
          {
            "isSignature": true,
            "lineNumber": 202,
            "text": "    base_blendfile: Path,"
          },
          {
            "isSignature": true,
            "lineNumber": 203,
            "text": "    project: Path,"
          },
          {
            "isSignature": true,
            "lineNumber": 204,
            "text": "    target: str,"
          },
          {
            "isSignature": true,
            "lineNumber": 205,
            "text": "    exclusion_filter: str,"
          },
          {
            "isSignature": true,
            "lineNumber": 206,
            "text": "    *,"
          },
          {
            "isSignature": true,
            "lineNumber": 207,
            "text": "    relative_only: bool,"
          },
          {
            "isSignature": true,
            "lineNumber": 208,
            "text": "    packer_class=submodules.pack.Packer,"
          },
          {
            "isSignature": true,
            "lineNumber": 209,
            "text": "    packer_kwargs: Optional[dict[Any, Any]] = None,"
          },
          {
            "isSignature": true,
            "lineNumber": 210,
            "text": ") -\u003e PackThread:"
          },
          {
            "lineNumber": 211,
            "text": "    \"\"\"Use BAT to copy the given file and dependencies to the target location."
          },
          {
            "lineNumber": 212
          },
          {
            "lineNumber": 213,
            "text": "    Runs BAT in a separate thread, and returns early. Use poll() to get updates"
          },
          {
            "lineNumber": 214,
            "text": "    \u0026 the final result."
          },
          {
            "lineNumber": 215,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 216,
            "text": "    global _running_packthread"
          },
          {
            "lineNumber": 217
          },
          {
            "lineNumber": 218,
            "text": "    with _packer_lock:"
          },
          {
            "lineNumber": 219,
            "text": "        if _running_packthread is not None:"
          },
          {
            "lineNumber": 220,
            "text": "            raise RuntimeError(\"other packing operation already in progress\")"
          },
          {
            "lineNumber": 221
          },
          {
            "lineNumber": 222,
            "text": "    # Due to issues with library overrides and unsynced pointers, it's quite"
          },
          {
            "lineNumber": 223,
            "text": "    # common for the Blender Animation Studio to get crashes of BAT. To avoid"
          },
          {
            "lineNumber": 224,
            "text": "    # these, Strict Pointer Mode is disabled."
          },
          {
            "lineNumber": 225,
            "text": "    submodules.blendfile.set_strict_pointer_mode(False)"
          },
          {
            "lineNumber": 226
          },
          {
            "lineNumber": 227,
            "text": "    log.info(\"BAT pack parameters:\")"
          },
          {
            "lineNumber": 228,
            "text": "    log.info(\"base_blendfile = %r\", base_blendfile)"
          },
          {
            "lineNumber": 229,
            "text": "    log.info(\"project        = %r\", project)"
          },
          {
            "lineNumber": 230,
            "text": "    log.info(\"target         = %r\", target)"
          },
          {
            "lineNumber": 231
          },
          {
            "lineNumber": 232,
            "text": "    if packer_kwargs is None:"
          },
          {
            "lineNumber": 233,
            "text": "        packer_kwargs = {}"
          },
          {
            "lineNumber": 234,
            "text": "    packer = packer_class("
          },
          {
            "lineNumber": 235,
            "text": "        base_blendfile,"
          },
          {
            "lineNumber": 236,
            "text": "        project,"
          },
          {
            "lineNumber": 237,
            "text": "        target,"
          },
          {
            "lineNumber": 238,
            "text": "        compress=True,"
          },
          {
            "lineNumber": 239,
            "text": "        relative_only=relative_only,"
          },
          {
            "lineNumber": 240,
            "text": "        **packer_kwargs,"
          },
          {
            "lineNumber": 241,
            "text": "    )"
          },
          {
            "lineNumber": 242,
            "text": "    if exclusion_filter:"
          },
          {
            "lineNumber": 243,
            "text": "        filter_parts = exclusion_filter.strip().split(\" \")"
          },
          {
            "lineNumber": 244,
            "text": "        packer.exclude(*filter_parts)"
          },
          {
            "lineNumber": 245
          },
          {
            "lineNumber": 246,
            "text": "    packthread = PackThread(packer=packer)"
          },
          {
            "lineNumber": 247,
            "text": "    with _packer_lock:"
          },
          {
            "lineNumber": 248,
            "text": "        _running_packthread = packthread"
          },
          {
            "lineNumber": 249
          },
          {
            "lineNumber": 250,
            "text": "    packthread.start()"
          },
          {
            "isSignature": true,
            "lineNumber": 251,
            "text": "    return packthread"
          },
          {
            "lineNumber": 252
          },
          {
            "lineNumber": 253
          },
          {
            "isSignature": true,
            "lineNumber": 254,
            "text": "def abort() -\u003e None:"
          },
          {
            "lineNumber": 255,
            "text": "    \"\"\"Abort a running copy() call."
          },
          {
            "lineNumber": 256
          },
          {
            "lineNumber": 257,
            "text": "    No-op when there is no running copy(). Can be called from any thread."
          },
          {
            "lineNumber": 258,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 264
          },
          "startPosition": {
            "column": 27,
            "line": 193
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.37749651074409485
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n\n\n    def _shaman_checkout_path(self) -\u003e PurePosixPath:\n        \"\"\"Construct the Shaman checkout path, aka Shaman Checkout ID.\n\n        Note that this may not be the actually used checkout ID, as that will be\n        made unique to this job by Flamenco Manager. That will be stored in\n        self.actual_shaman_checkout_path after the Shaman checkout is actually\n        done.\n        \"\"\"\n        assert self.job is not None\n\n        # TODO: get project name from preferences/GUI and insert that here too.\n        return PurePosixPath(f\"{self.job.name}\")\n\n    def _bat_pack_shaman(self, context: bpy.types.Context, blendfile: Path) -\u003e None:\n        \"\"\"Use the Manager's Shaman API to submit the BAT pack.\n\n        :return: the filesystem path of the blend file, for in the render job definition.\n        \"\"\"\n        from .bat import (\n            interface as bat_interface,\n            shaman as bat_shaman,\n        )\n\n        assert self.job is not None\n        self.log.info(\"Sending BAT pack to Shaman\")\n\n        prefs = preferences.get(context)\n        project_path: Path = prefs.project_root()\n\n        self.packthread = bat_interface.copy(\n            base_blendfile=blendfile,\n            project=project_path,\n            target=\"/\",  # Target directory irrelevant for Shaman transfers.\n            exclusion_filter=\"\",  # TODO: get from GUI.\n            relative_only=True,  # TODO: get from GUI.\n            packer_class=bat_shaman.Packer,\n            packer_kwargs=dict(\n                api_client=self.get_api_client(context),\n                checkout_path=self._shaman_checkout_path(),\n            ),\n        )\n\n        # We cannot assume the blendfile location is known until the Shaman\n        # checkout has actually been created.\n\n    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 448
          },
          {
            "lineNumber": 449
          },
          {
            "lineNumber": 450,
            "text": "    def _shaman_checkout_path(self) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 451,
            "text": "        \"\"\"Construct the Shaman checkout path, aka Shaman Checkout ID."
          },
          {
            "lineNumber": 452
          },
          {
            "lineNumber": 453,
            "text": "        Note that this may not be the actually used checkout ID, as that will be"
          },
          {
            "lineNumber": 454,
            "text": "        made unique to this job by Flamenco Manager. That will be stored in"
          },
          {
            "lineNumber": 455,
            "text": "        self.actual_shaman_checkout_path after the Shaman checkout is actually"
          },
          {
            "lineNumber": 456,
            "text": "        done."
          },
          {
            "lineNumber": 457,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 458,
            "text": "        assert self.job is not None"
          },
          {
            "lineNumber": 459
          },
          {
            "lineNumber": 460,
            "text": "        # TODO: get project name from preferences/GUI and insert that here too."
          },
          {
            "lineNumber": 461,
            "text": "        return PurePosixPath(f\"{self.job.name}\")"
          },
          {
            "lineNumber": 462
          },
          {
            "lineNumber": 463,
            "text": "    def _bat_pack_shaman(self, context: bpy.types.Context, blendfile: Path) -\u003e None:"
          },
          {
            "lineNumber": 464,
            "text": "        \"\"\"Use the Manager's Shaman API to submit the BAT pack."
          },
          {
            "lineNumber": 465
          },
          {
            "lineNumber": 466,
            "text": "        :return: the filesystem path of the blend file, for in the render job definition."
          },
          {
            "lineNumber": 467,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 468,
            "text": "        from .bat import ("
          },
          {
            "lineNumber": 469,
            "text": "            interface as bat_interface,"
          },
          {
            "lineNumber": 470,
            "text": "            shaman as bat_shaman,"
          },
          {
            "lineNumber": 471,
            "text": "        )"
          },
          {
            "lineNumber": 472
          },
          {
            "lineNumber": 473,
            "text": "        assert self.job is not None"
          },
          {
            "lineNumber": 474,
            "text": "        self.log.info(\"Sending BAT pack to Shaman\")"
          },
          {
            "lineNumber": 475
          },
          {
            "lineNumber": 476,
            "text": "        prefs = preferences.get(context)"
          },
          {
            "lineNumber": 477,
            "text": "        project_path: Path = prefs.project_root()"
          },
          {
            "lineNumber": 478
          },
          {
            "lineNumber": 479,
            "text": "        self.packthread = bat_interface.copy("
          },
          {
            "lineNumber": 480,
            "text": "            base_blendfile=blendfile,"
          },
          {
            "lineNumber": 481,
            "text": "            project=project_path,"
          },
          {
            "lineNumber": 482,
            "text": "            target=\"/\",  # Target directory irrelevant for Shaman transfers."
          },
          {
            "lineNumber": 483,
            "text": "            exclusion_filter=\"\",  # TODO: get from GUI."
          },
          {
            "lineNumber": 484,
            "text": "            relative_only=True,  # TODO: get from GUI."
          },
          {
            "lineNumber": 485,
            "text": "            packer_class=bat_shaman.Packer,"
          },
          {
            "lineNumber": 486,
            "text": "            packer_kwargs=dict("
          },
          {
            "lineNumber": 487,
            "text": "                api_client=self.get_api_client(context),"
          },
          {
            "lineNumber": 488,
            "text": "                checkout_path=self._shaman_checkout_path(),"
          },
          {
            "lineNumber": 489,
            "text": "            ),"
          },
          {
            "lineNumber": 490,
            "text": "        )"
          },
          {
            "lineNumber": 491
          },
          {
            "lineNumber": 492,
            "text": "        # We cannot assume the blendfile location is known until the Shaman"
          },
          {
            "lineNumber": 493,
            "text": "        # checkout has actually been created."
          },
          {
            "lineNumber": 494
          },
          {
            "lineNumber": 495,
            "text": "    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:"
          },
          {
            "lineNumber": 496,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.3741035461425781
    },
    {
      "codeBlock": {
        "contents": "# Mypy doesn't understand that submodules.pack.Packer exists.\nclass Packer(submodules.pack.Packer):  # type: ignore\n    \"\"\"Creates BAT Packs on a Shaman server.\"\"\"\n\n    def __init__(\n        self,\n        blendfile: Path,\n        project_root: Path,\n        target: str,\n        *,\n        api_client: _ApiClient,\n        checkout_path: str,\n        **kwargs: dict[Any, Any],\n    ) -\u003e None:\n        \"\"\"Constructor\n\n        :param target: mock target root directory to construct project-relative paths.\n        \"\"\"\n        super().__init__(blendfile, project_root, target, **kwargs)\n        self.checkout_path = checkout_path\n        self.api_client = api_client\n        self.shaman_transferrer: Optional[Transferrer] = None\n\n    # Mypy doesn't understand that submodules.transfer.FileTransferer exists.\n    def _create_file_transferer(self) -\u003e submodules.transfer.FileTransferer:  # type: ignore\n        self.shaman_transferrer = Transferrer(\n            self.api_client, self.project, self.checkout_path\n        )\n        return self.shaman_transferrer\n\n    def _make_target_path(self, target: str) -\u003e PurePath:\n        return _root_path()\n\n    @property\n    def output_path(self) -\u003e PurePath:\n        \"\"\"The path of the packed blend file in the target directory.\"\"\"\n        assert self._output_path is not None\n\n        rel_output = self._output_path.relative_to(self._target_path)\n        out_path: PurePath = self.actual_checkout_path / rel_output\n        return out_path\n\n    @property\n    def actual_checkout_path(self) -\u003e PurePosixPath:\n        \"\"\"The actual Shaman checkout path.\n\n        Only valid after packing is complete. Shaman ensures that the checkout\n        is unique, and thus the actual path can be different than the requested\n        one.\n        \"\"\"\n        assert self.shaman_transferrer is not None\n        return PurePosixPath(self.shaman_transferrer.checkout_path)\n\n    def execute(self):\n        try:\n            super().execute()",
        "detailedLines": [
          {
            "lineNumber": 37,
            "text": "# Mypy doesn't understand that submodules.pack.Packer exists."
          },
          {
            "isSignature": true,
            "lineNumber": 38,
            "text": "class Packer(submodules.pack.Packer):  # type: ignore"
          },
          {
            "lineNumber": 39,
            "text": "    \"\"\"Creates BAT Packs on a Shaman server.\"\"\""
          },
          {
            "lineNumber": 40
          },
          {
            "lineNumber": 41,
            "text": "    def __init__("
          },
          {
            "lineNumber": 42,
            "text": "        self,"
          },
          {
            "lineNumber": 43,
            "text": "        blendfile: Path,"
          },
          {
            "lineNumber": 44,
            "text": "        project_root: Path,"
          },
          {
            "lineNumber": 45,
            "text": "        target: str,"
          },
          {
            "lineNumber": 46,
            "text": "        *,"
          },
          {
            "lineNumber": 47,
            "text": "        api_client: _ApiClient,"
          },
          {
            "lineNumber": 48,
            "text": "        checkout_path: str,"
          },
          {
            "lineNumber": 49,
            "text": "        **kwargs: dict[Any, Any],"
          },
          {
            "lineNumber": 50,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 51,
            "text": "        \"\"\"Constructor"
          },
          {
            "lineNumber": 52
          },
          {
            "lineNumber": 53,
            "text": "        :param target: mock target root directory to construct project-relative paths."
          },
          {
            "lineNumber": 54,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 55,
            "text": "        super().__init__(blendfile, project_root, target, **kwargs)"
          },
          {
            "lineNumber": 56,
            "text": "        self.checkout_path = checkout_path"
          },
          {
            "lineNumber": 57,
            "text": "        self.api_client = api_client"
          },
          {
            "lineNumber": 58,
            "text": "        self.shaman_transferrer: Optional[Transferrer] = None"
          },
          {
            "lineNumber": 59
          },
          {
            "lineNumber": 60,
            "text": "    # Mypy doesn't understand that submodules.transfer.FileTransferer exists."
          },
          {
            "lineNumber": 61,
            "text": "    def _create_file_transferer(self) -\u003e submodules.transfer.FileTransferer:  # type: ignore"
          },
          {
            "lineNumber": 62,
            "text": "        self.shaman_transferrer = Transferrer("
          },
          {
            "lineNumber": 63,
            "text": "            self.api_client, self.project, self.checkout_path"
          },
          {
            "lineNumber": 64,
            "text": "        )"
          },
          {
            "lineNumber": 65,
            "text": "        return self.shaman_transferrer"
          },
          {
            "lineNumber": 66
          },
          {
            "lineNumber": 67,
            "text": "    def _make_target_path(self, target: str) -\u003e PurePath:"
          },
          {
            "lineNumber": 68,
            "text": "        return _root_path()"
          },
          {
            "lineNumber": 69
          },
          {
            "lineNumber": 70,
            "text": "    @property"
          },
          {
            "lineNumber": 71,
            "text": "    def output_path(self) -\u003e PurePath:"
          },
          {
            "lineNumber": 72,
            "text": "        \"\"\"The path of the packed blend file in the target directory.\"\"\""
          },
          {
            "lineNumber": 73,
            "text": "        assert self._output_path is not None"
          },
          {
            "lineNumber": 74
          },
          {
            "lineNumber": 75,
            "text": "        rel_output = self._output_path.relative_to(self._target_path)"
          },
          {
            "lineNumber": 76,
            "text": "        out_path: PurePath = self.actual_checkout_path / rel_output"
          },
          {
            "lineNumber": 77,
            "text": "        return out_path"
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "    @property"
          },
          {
            "lineNumber": 80,
            "text": "    def actual_checkout_path(self) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 81,
            "text": "        \"\"\"The actual Shaman checkout path."
          },
          {
            "lineNumber": 82
          },
          {
            "lineNumber": 83,
            "text": "        Only valid after packing is complete. Shaman ensures that the checkout"
          },
          {
            "lineNumber": 84,
            "text": "        is unique, and thus the actual path can be different than the requested"
          },
          {
            "lineNumber": 85,
            "text": "        one."
          },
          {
            "lineNumber": 86,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 87,
            "text": "        assert self.shaman_transferrer is not None"
          },
          {
            "lineNumber": 88,
            "text": "        return PurePosixPath(self.shaman_transferrer.checkout_path)"
          },
          {
            "lineNumber": 89
          },
          {
            "lineNumber": 90,
            "text": "    def execute(self):"
          },
          {
            "lineNumber": 91,
            "text": "        try:"
          },
          {
            "lineNumber": 92,
            "text": "            super().execute()"
          }
        ],
        "range": {
          "endPosition": {
            "line": 95
          },
          "startPosition": {
            "column": 72,
            "line": 33
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.32023191452026367
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\n\n\n    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:\n        from .bat import interface as bat_interface\n\n        if isinstance(msg, bat_interface.MsgDone):\n            if self.blendfile_on_farm is None:\n                # Adjust the blendfile to match the Shaman checkout path. Shaman\n                # may have checked out at a different location than we\n                # requested.\n                #\n                # Manager automatically creates a variable \"jobs\" that will\n                # resolve to the job storage directory.\n                self.blendfile_on_farm = PurePosixPath(\"{jobs}\") / msg.output_path\n\n            self.actual_shaman_checkout_path = msg.actual_checkout_path\n            self._submit_job(context)\n            return self._quit(context)\n\n        if isinstance(msg, bat_interface.MsgException):\n            self.log.error(\"Error performing BAT pack: %s\", msg.ex)\n            self.report({\"ERROR\"}, \"Error performing BAT pack: %s\" % msg.ex)\n\n            # This was an exception caught at the top level of the thread, so\n            # the packing thread itself has stopped.\n            return self._quit(context)\n\n        if isinstance(msg, bat_interface.MsgSetWMAttribute):\n            wm = context.window_manager\n            setattr(wm, msg.attribute_name, msg.value)\n\n        return {\"RUNNING_MODAL\"}\n\n    def _use_blendfile_directly(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e None:\n        # The temporary '.flamenco.blend' file should not be deleted, as it\n        # will be used directly by the render job.\n        self.temp_blendfile = None\n\n        # The blend file is contained in the job storage path, no need to\n        # copy anything.\n        self.blendfile_on_farm = bpathlib.make_absolute(blendfile)\n\n        # No Shaman is involved when using the file directly.\n        self.actual_shaman_checkout_path = None\n\n        self._submit_job(context)\n\n    def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 493
          },
          {
            "lineNumber": 494
          },
          {
            "lineNumber": 495,
            "text": "    def _on_bat_pack_msg(self, context: bpy.types.Context, msg: _Message) -\u003e set[str]:"
          },
          {
            "lineNumber": 496,
            "text": "        from .bat import interface as bat_interface"
          },
          {
            "lineNumber": 497
          },
          {
            "lineNumber": 498,
            "text": "        if isinstance(msg, bat_interface.MsgDone):"
          },
          {
            "lineNumber": 499,
            "text": "            if self.blendfile_on_farm is None:"
          },
          {
            "lineNumber": 500,
            "text": "                # Adjust the blendfile to match the Shaman checkout path. Shaman"
          },
          {
            "lineNumber": 501,
            "text": "                # may have checked out at a different location than we"
          },
          {
            "lineNumber": 502,
            "text": "                # requested."
          },
          {
            "lineNumber": 503,
            "text": "                #"
          },
          {
            "lineNumber": 504,
            "text": "                # Manager automatically creates a variable \"jobs\" that will"
          },
          {
            "lineNumber": 505,
            "text": "                # resolve to the job storage directory."
          },
          {
            "lineNumber": 506,
            "text": "                self.blendfile_on_farm = PurePosixPath(\"{jobs}\") / msg.output_path"
          },
          {
            "lineNumber": 507
          },
          {
            "lineNumber": 508,
            "text": "            self.actual_shaman_checkout_path = msg.actual_checkout_path"
          },
          {
            "lineNumber": 509,
            "text": "            self._submit_job(context)"
          },
          {
            "lineNumber": 510,
            "text": "            return self._quit(context)"
          },
          {
            "lineNumber": 511
          },
          {
            "lineNumber": 512,
            "text": "        if isinstance(msg, bat_interface.MsgException):"
          },
          {
            "lineNumber": 513,
            "text": "            self.log.error(\"Error performing BAT pack: %s\", msg.ex)"
          },
          {
            "lineNumber": 514,
            "text": "            self.report({\"ERROR\"}, \"Error performing BAT pack: %s\" % msg.ex)"
          },
          {
            "lineNumber": 515
          },
          {
            "lineNumber": 516,
            "text": "            # This was an exception caught at the top level of the thread, so"
          },
          {
            "lineNumber": 517,
            "text": "            # the packing thread itself has stopped."
          },
          {
            "lineNumber": 518,
            "text": "            return self._quit(context)"
          },
          {
            "lineNumber": 519
          },
          {
            "lineNumber": 520,
            "text": "        if isinstance(msg, bat_interface.MsgSetWMAttribute):"
          },
          {
            "lineNumber": 521,
            "text": "            wm = context.window_manager"
          },
          {
            "lineNumber": 522,
            "text": "            setattr(wm, msg.attribute_name, msg.value)"
          },
          {
            "lineNumber": 523
          },
          {
            "lineNumber": 524,
            "text": "        return {\"RUNNING_MODAL\"}"
          },
          {
            "lineNumber": 525
          },
          {
            "lineNumber": 526,
            "text": "    def _use_blendfile_directly("
          },
          {
            "lineNumber": 527,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 528,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 529,
            "text": "        # The temporary '.flamenco.blend' file should not be deleted, as it"
          },
          {
            "lineNumber": 530,
            "text": "        # will be used directly by the render job."
          },
          {
            "lineNumber": 531,
            "text": "        self.temp_blendfile = None"
          },
          {
            "lineNumber": 532
          },
          {
            "lineNumber": 533,
            "text": "        # The blend file is contained in the job storage path, no need to"
          },
          {
            "lineNumber": 534,
            "text": "        # copy anything."
          },
          {
            "lineNumber": 535,
            "text": "        self.blendfile_on_farm = bpathlib.make_absolute(blendfile)"
          },
          {
            "lineNumber": 536
          },
          {
            "lineNumber": 537,
            "text": "        # No Shaman is involved when using the file directly."
          },
          {
            "lineNumber": 538,
            "text": "        self.actual_shaman_checkout_path = None"
          },
          {
            "lineNumber": 539
          },
          {
            "lineNumber": 540,
            "text": "        self._submit_job(context)"
          },
          {
            "lineNumber": 541
          },
          {
            "lineNumber": 542,
            "text": "    def _prepare_job_for_submission(self, context: bpy.types.Context) -\u003e bool:"
          },
          {
            "lineNumber": 543,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            }
          ]
        }
      },
      "score": 0.312574565410614
    },
    {
      "codeBlock": {
        "contents": "class PackThread(threading.Thread):\n    queue: queue.SimpleQueue[Message]\n\n    # MyPy doesn't understand the way BAT subpackages are imported.\n    def __init__(self, packer: submodules.pack.Packer) -\u003e None:  # type: ignore\n        # Quitting Blender should abort the transfer (instead of hanging until\n        # the transfer is done), hence daemon=True.\n        super().__init__(daemon=True, name=\"PackThread\")\n\n        self.queue = queue.SimpleQueue()\n\n        self.packer = packer\n        self.packer.progress_cb = BatProgress(queue=self.queue)\n\n    def run(self) -\u003e None:\n        global _running_packthread\n\n        try:\n            self._run()\n        except BaseException as ex:\n            self._set_bat_status(\"ABORTED\")\n            log.exception(\"Error packing with BAT: %s\", ex)\n            self.queue.put(MsgException(ex=ex))\n        finally:\n            with _packer_lock:\n                _running_packthread = None\n\n    def _run(self) -\u003e None:\n        with self.packer:\n            log.debug(\"awaiting strategise\")\n            self._set_bat_status(\"INVESTIGATING\")\n            self.packer.strategise()\n\n            log.debug(\"awaiting execute\")\n            self._set_bat_status(\"TRANSFERRING\")\n            self.packer.execute()\n\n            log.debug(\"done\")\n            self._set_bat_status(\"DONE\")\n\n            msg = MsgDone(\n                self.packer.output_path,\n                self.packer.missing_files,\n                getattr(self.packer, \"actual_checkout_path\", None),\n            )\n            self.queue.put(msg)\n\n    def _set_bat_status(self, status: str) -\u003e None:\n        self.queue.put(MsgSetWMAttribute(\"flamenco_bat_status\", status))\n\n    def poll(self, timeout: Optional[int] = None) -\u003e Optional[Message]:\n        \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 130,
            "text": "class PackThread(threading.Thread):"
          },
          {
            "lineNumber": 131,
            "text": "    queue: queue.SimpleQueue[Message]"
          },
          {
            "lineNumber": 132
          },
          {
            "lineNumber": 133,
            "text": "    # MyPy doesn't understand the way BAT subpackages are imported."
          },
          {
            "lineNumber": 134,
            "text": "    def __init__(self, packer: submodules.pack.Packer) -\u003e None:  # type: ignore"
          },
          {
            "lineNumber": 135,
            "text": "        # Quitting Blender should abort the transfer (instead of hanging until"
          },
          {
            "lineNumber": 136,
            "text": "        # the transfer is done), hence daemon=True."
          },
          {
            "lineNumber": 137,
            "text": "        super().__init__(daemon=True, name=\"PackThread\")"
          },
          {
            "lineNumber": 138
          },
          {
            "lineNumber": 139,
            "text": "        self.queue = queue.SimpleQueue()"
          },
          {
            "lineNumber": 140
          },
          {
            "lineNumber": 141,
            "text": "        self.packer = packer"
          },
          {
            "lineNumber": 142,
            "text": "        self.packer.progress_cb = BatProgress(queue=self.queue)"
          },
          {
            "lineNumber": 143
          },
          {
            "lineNumber": 144,
            "text": "    def run(self) -\u003e None:"
          },
          {
            "lineNumber": 145,
            "text": "        global _running_packthread"
          },
          {
            "lineNumber": 146
          },
          {
            "lineNumber": 147,
            "text": "        try:"
          },
          {
            "lineNumber": 148,
            "text": "            self._run()"
          },
          {
            "lineNumber": 149,
            "text": "        except BaseException as ex:"
          },
          {
            "lineNumber": 150,
            "text": "            self._set_bat_status(\"ABORTED\")"
          },
          {
            "lineNumber": 151,
            "text": "            log.exception(\"Error packing with BAT: %s\", ex)"
          },
          {
            "lineNumber": 152,
            "text": "            self.queue.put(MsgException(ex=ex))"
          },
          {
            "lineNumber": 153,
            "text": "        finally:"
          },
          {
            "lineNumber": 154,
            "text": "            with _packer_lock:"
          },
          {
            "lineNumber": 155,
            "text": "                _running_packthread = None"
          },
          {
            "lineNumber": 156
          },
          {
            "lineNumber": 157,
            "text": "    def _run(self) -\u003e None:"
          },
          {
            "lineNumber": 158,
            "text": "        with self.packer:"
          },
          {
            "lineNumber": 159,
            "text": "            log.debug(\"awaiting strategise\")"
          },
          {
            "lineNumber": 160,
            "text": "            self._set_bat_status(\"INVESTIGATING\")"
          },
          {
            "lineNumber": 161,
            "text": "            self.packer.strategise()"
          },
          {
            "lineNumber": 162
          },
          {
            "lineNumber": 163,
            "text": "            log.debug(\"awaiting execute\")"
          },
          {
            "lineNumber": 164,
            "text": "            self._set_bat_status(\"TRANSFERRING\")"
          },
          {
            "lineNumber": 165,
            "text": "            self.packer.execute()"
          },
          {
            "lineNumber": 166
          },
          {
            "lineNumber": 167,
            "text": "            log.debug(\"done\")"
          },
          {
            "lineNumber": 168,
            "text": "            self._set_bat_status(\"DONE\")"
          },
          {
            "lineNumber": 169
          },
          {
            "lineNumber": 170,
            "text": "            msg = MsgDone("
          },
          {
            "lineNumber": 171,
            "text": "                self.packer.output_path,"
          },
          {
            "lineNumber": 172,
            "text": "                self.packer.missing_files,"
          },
          {
            "lineNumber": 173,
            "text": "                getattr(self.packer, \"actual_checkout_path\", None),"
          },
          {
            "lineNumber": 174,
            "text": "            )"
          },
          {
            "lineNumber": 175,
            "text": "            self.queue.put(msg)"
          },
          {
            "lineNumber": 176
          },
          {
            "lineNumber": 177,
            "text": "    def _set_bat_status(self, status: str) -\u003e None:"
          },
          {
            "lineNumber": 178,
            "text": "        self.queue.put(MsgSetWMAttribute(\"flamenco_bat_status\", status))"
          },
          {
            "lineNumber": 179
          },
          {
            "lineNumber": 180,
            "text": "    def poll(self, timeout: Optional[int] = None) -\u003e Optional[Message]:"
          },
          {
            "lineNumber": 181,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 193
          },
          "startPosition": {
            "column": 32,
            "line": 126
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.2990303933620453
    },
    {
      "codeBlock": {
        "contents": "def is_file_inside_job_storage(context: bpy.types.Context, blendfile: Path) -\u003e bool:\n    \"\"\"Check whether current blend file is inside the storage path.\n\n    :return: True when the current blend file is inside the Flamenco job storage\n        directory already. In this case it won't be BAT-packed, as it's assumed\n        the job storage dir is accessible by the workers already.\n    \"\"\"\n\n    blendfile = bpathlib.make_absolute(blendfile)\n\n    info = manager_info.load_cached()\n    if not info:\n        raise RuntimeError(\"Flamenco Manager info unknown, please refresh.\")\n\n    job_storage = bpathlib.make_absolute(Path(info.shared_storage.location))\n\n    log.info(\"Checking whether the file is already inside the job storage\")\n    log.info(\"    file   : %s\", blendfile)\n    log.info(\"    storage: %s\", job_storage)\n\n    try:\n        blendfile.relative_to(job_storage)\n    except ValueError:\n        return False\n    return True",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 134,
            "text": "def is_file_inside_job_storage(context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 135,
            "text": "    \"\"\"Check whether current blend file is inside the storage path."
          },
          {
            "lineNumber": 136
          },
          {
            "lineNumber": 137,
            "text": "    :return: True when the current blend file is inside the Flamenco job storage"
          },
          {
            "lineNumber": 138,
            "text": "        directory already. In this case it won't be BAT-packed, as it's assumed"
          },
          {
            "lineNumber": 139,
            "text": "        the job storage dir is accessible by the workers already."
          },
          {
            "lineNumber": 140,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 141
          },
          {
            "lineNumber": 142,
            "text": "    blendfile = bpathlib.make_absolute(blendfile)"
          },
          {
            "lineNumber": 143
          },
          {
            "lineNumber": 144,
            "text": "    info = manager_info.load_cached()"
          },
          {
            "lineNumber": 145,
            "text": "    if not info:"
          },
          {
            "lineNumber": 146,
            "text": "        raise RuntimeError(\"Flamenco Manager info unknown, please refresh.\")"
          },
          {
            "lineNumber": 147
          },
          {
            "lineNumber": 148,
            "text": "    job_storage = bpathlib.make_absolute(Path(info.shared_storage.location))"
          },
          {
            "lineNumber": 149
          },
          {
            "lineNumber": 150,
            "text": "    log.info(\"Checking whether the file is already inside the job storage\")"
          },
          {
            "lineNumber": 151,
            "text": "    log.info(\"    file   : %s\", blendfile)"
          },
          {
            "lineNumber": 152,
            "text": "    log.info(\"    storage: %s\", job_storage)"
          },
          {
            "lineNumber": 153
          },
          {
            "lineNumber": 154,
            "text": "    try:"
          },
          {
            "lineNumber": 155,
            "text": "        blendfile.relative_to(job_storage)"
          },
          {
            "lineNumber": 156,
            "text": "    except ValueError:"
          },
          {
            "lineNumber": 157,
            "text": "        return False"
          },
          {
            "isSignature": true,
            "lineNumber": 158,
            "text": "    return True"
          }
        ],
        "range": {
          "endPosition": {
            "column": 15,
            "line": 157
          },
          "startPosition": {
            "column": 42,
            "line": 130
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.25355803966522217
    },
    {
      "codeBlock": {
        "contents": "def _root_path() -\u003e PurePath:\n    \"\"\"Return an arbitrary root path for the current platform.\n\n    When packing, BAT needs to know the \"target path\", and for Shaman use this\n    is kind of irrelevant. Any path will do, as long as it's absolute.\n    \"\"\"\n\n    if platform.system() == \"Windows\":\n        # A path on Windows can only be absolute if it has a drive letter. The\n        # letter itself doesn't matter, as it'll only be used to compute\n        # relative paths between various files rooted here.\n        return PureWindowsPath(\"X:/\")\n    return PurePosixPath(\"/\")\n\n\ndef _root_path_strip(path: PurePath) -\u003e PurePosixPath:\n    \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 504,
            "text": "def _root_path() -\u003e PurePath:"
          },
          {
            "lineNumber": 505,
            "text": "    \"\"\"Return an arbitrary root path for the current platform."
          },
          {
            "lineNumber": 506
          },
          {
            "lineNumber": 507,
            "text": "    When packing, BAT needs to know the \"target path\", and for Shaman use this"
          },
          {
            "lineNumber": 508,
            "text": "    is kind of irrelevant. Any path will do, as long as it's absolute."
          },
          {
            "lineNumber": 509,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 510
          },
          {
            "lineNumber": 511,
            "text": "    if platform.system() == \"Windows\":"
          },
          {
            "lineNumber": 512,
            "text": "        # A path on Windows can only be absolute if it has a drive letter. The"
          },
          {
            "lineNumber": 513,
            "text": "        # letter itself doesn't matter, as it'll only be used to compute"
          },
          {
            "lineNumber": 514,
            "text": "        # relative paths between various files rooted here."
          },
          {
            "lineNumber": 515,
            "text": "        return PureWindowsPath(\"X:/\")"
          },
          {
            "isSignature": true,
            "lineNumber": 516,
            "text": "    return PurePosixPath(\"/\")"
          },
          {
            "lineNumber": 517
          },
          {
            "lineNumber": 518
          },
          {
            "isSignature": true,
            "lineNumber": 519,
            "text": "def _root_path_strip(path: PurePath) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 520,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 530
          },
          "startPosition": {
            "column": 68,
            "line": 500
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.23922620713710785
    },
    {
      "codeBlock": {
        "contents": "# MyPy doesn't understand the way BAT subpackages are imported.\nclass BatProgress(submodules.progress.Callback):  # type: ignore\n    \"\"\"Report progress of BAT Packing to the given queue.\"\"\"\n\n    def __init__(self, queue: queue.SimpleQueue[Message]) -\u003e None:\n        super().__init__()\n        self.queue = queue\n\n    def _set_attr(self, attr: str, value: Any) -\u003e None:\n        msg = MsgSetWMAttribute(attr, value)\n        self.queue.put(msg)\n\n    def _txt(self, msg: str) -\u003e None:\n        \"\"\"Set a text in a thread-safe way.\"\"\"\n        self._set_attr(\"flamenco_bat_status_txt\", msg)\n\n    def _status(self, status: str) -\u003e None:\n        \"\"\"Set the flamenco_bat_status property in a thread-safe way.\"\"\"\n        self._set_attr(\"flamenco_bat_status\", status)\n\n    def _progress(self, progress: int) -\u003e None:\n        \"\"\"Set the flamenco_bat_progress property in a thread-safe way.\"\"\"\n        self._set_attr(\"flamenco_bat_progress\", progress)\n        msg = MsgProgress(percentage=progress)\n        self.queue.put(msg)\n\n    def pack_start(self) -\u003e None:\n        self._txt(\"Starting BAT Pack operation\")\n\n    def pack_done(\n        self, output_blendfile: Path, missing_files: typing.Set[Path]\n    ) -\u003e None:\n        if missing_files:\n            self._txt(\"There were %d missing files\" % len(missing_files))\n            self._log_missing_files(missing_files)\n        else:\n            self._txt(\"Pack of %s done\" % output_blendfile.name)\n\n    def pack_aborted(self, reason: str) -\u003e None:\n        self._txt(\"Aborted: %s\" % reason)\n        self._status(\"ABORTED\")\n\n    def trace_blendfile(self, filename: Path) -\u003e None:\n        \"\"\"Called for every blendfile opened when tracing dependencies.\"\"\"\n        self._txt(\"Inspecting %s\" % filename.name)\n\n    def trace_asset(self, filename: Path) -\u003e None:\n        if filename.stem == \".blend\":\n            return\n        self._txt(\"Found asset %s\" % filename.name)\n\n    def rewrite_blendfile(self, orig_filename: Path) -\u003e None:\n        self._txt(\"Rewriting %s\" % orig_filename.name)",
        "detailedLines": [
          {
            "lineNumber": 57,
            "text": "# MyPy doesn't understand the way BAT subpackages are imported."
          },
          {
            "isSignature": true,
            "lineNumber": 58,
            "text": "class BatProgress(submodules.progress.Callback):  # type: ignore"
          },
          {
            "lineNumber": 59,
            "text": "    \"\"\"Report progress of BAT Packing to the given queue.\"\"\""
          },
          {
            "lineNumber": 60
          },
          {
            "lineNumber": 61,
            "text": "    def __init__(self, queue: queue.SimpleQueue[Message]) -\u003e None:"
          },
          {
            "lineNumber": 62,
            "text": "        super().__init__()"
          },
          {
            "lineNumber": 63,
            "text": "        self.queue = queue"
          },
          {
            "lineNumber": 64
          },
          {
            "lineNumber": 65,
            "text": "    def _set_attr(self, attr: str, value: Any) -\u003e None:"
          },
          {
            "lineNumber": 66,
            "text": "        msg = MsgSetWMAttribute(attr, value)"
          },
          {
            "lineNumber": 67,
            "text": "        self.queue.put(msg)"
          },
          {
            "lineNumber": 68
          },
          {
            "lineNumber": 69,
            "text": "    def _txt(self, msg: str) -\u003e None:"
          },
          {
            "lineNumber": 70,
            "text": "        \"\"\"Set a text in a thread-safe way.\"\"\""
          },
          {
            "lineNumber": 71,
            "text": "        self._set_attr(\"flamenco_bat_status_txt\", msg)"
          },
          {
            "lineNumber": 72
          },
          {
            "lineNumber": 73,
            "text": "    def _status(self, status: str) -\u003e None:"
          },
          {
            "lineNumber": 74,
            "text": "        \"\"\"Set the flamenco_bat_status property in a thread-safe way.\"\"\""
          },
          {
            "lineNumber": 75,
            "text": "        self._set_attr(\"flamenco_bat_status\", status)"
          },
          {
            "lineNumber": 76
          },
          {
            "lineNumber": 77,
            "text": "    def _progress(self, progress: int) -\u003e None:"
          },
          {
            "lineNumber": 78,
            "text": "        \"\"\"Set the flamenco_bat_progress property in a thread-safe way.\"\"\""
          },
          {
            "lineNumber": 79,
            "text": "        self._set_attr(\"flamenco_bat_progress\", progress)"
          },
          {
            "lineNumber": 80,
            "text": "        msg = MsgProgress(percentage=progress)"
          },
          {
            "lineNumber": 81,
            "text": "        self.queue.put(msg)"
          },
          {
            "lineNumber": 82
          },
          {
            "lineNumber": 83,
            "text": "    def pack_start(self) -\u003e None:"
          },
          {
            "lineNumber": 84,
            "text": "        self._txt(\"Starting BAT Pack operation\")"
          },
          {
            "lineNumber": 85
          },
          {
            "lineNumber": 86,
            "text": "    def pack_done("
          },
          {
            "lineNumber": 87,
            "text": "        self, output_blendfile: Path, missing_files: typing.Set[Path]"
          },
          {
            "lineNumber": 88,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 89,
            "text": "        if missing_files:"
          },
          {
            "lineNumber": 90,
            "text": "            self._txt(\"There were %d missing files\" % len(missing_files))"
          },
          {
            "lineNumber": 91,
            "text": "            self._log_missing_files(missing_files)"
          },
          {
            "lineNumber": 92,
            "text": "        else:"
          },
          {
            "lineNumber": 93,
            "text": "            self._txt(\"Pack of %s done\" % output_blendfile.name)"
          },
          {
            "lineNumber": 94
          },
          {
            "lineNumber": 95,
            "text": "    def pack_aborted(self, reason: str) -\u003e None:"
          },
          {
            "lineNumber": 96,
            "text": "        self._txt(\"Aborted: %s\" % reason)"
          },
          {
            "lineNumber": 97,
            "text": "        self._status(\"ABORTED\")"
          },
          {
            "lineNumber": 98
          },
          {
            "lineNumber": 99,
            "text": "    def trace_blendfile(self, filename: Path) -\u003e None:"
          },
          {
            "lineNumber": 100,
            "text": "        \"\"\"Called for every blendfile opened when tracing dependencies.\"\"\""
          },
          {
            "lineNumber": 101,
            "text": "        self._txt(\"Inspecting %s\" % filename.name)"
          },
          {
            "lineNumber": 102
          },
          {
            "lineNumber": 103,
            "text": "    def trace_asset(self, filename: Path) -\u003e None:"
          },
          {
            "lineNumber": 104,
            "text": "        if filename.stem == \".blend\":"
          },
          {
            "lineNumber": 105,
            "text": "            return"
          },
          {
            "lineNumber": 106,
            "text": "        self._txt(\"Found asset %s\" % filename.name)"
          },
          {
            "lineNumber": 107
          },
          {
            "lineNumber": 108,
            "text": "    def rewrite_blendfile(self, orig_filename: Path) -\u003e None:"
          },
          {
            "lineNumber": 109,
            "text": "        self._txt(\"Rewriting %s\" % orig_filename.name)"
          }
        ],
        "range": {
          "endPosition": {
            "line": 126
          },
          "startPosition": {
            "column": 56,
            "line": 53
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.2355414628982544
    },
    {
      "codeBlock": {
        "contents": "class JobTypePropertyGroup:\n\n\n    @staticmethod\n    def last_n_dir_parts(n: int, filepath: Union[str, Path, None] = None) -\u003e Path:\n        \"\"\"Return the last &#96;n&#96; parts of the directory of &#96;filepath&#96;.\n\n        If &#96;n&#96; is 0, returns an empty &#96;Path()&#96;.\n        If &#96;filepath&#96; is None, uses bpy.data.filepath instead.\n\n        \u003e\u003e\u003e str(last_n_dir_parts(2, \"/path/to/some/file.blend\"))\n        \"to/some\"\n\n        Always returns a relative path:\n        \u003e\u003e\u003e str(last_n_dir_parts(200, \"C:\\\\path\\\\to\\\\some\\\\file.blend\"))\n        \"path\\\\to\\\\some\"\n        \"\"\"\n\n        if n \u003c= 0:\n            return Path()\n\n        if filepath is None:\n            filepath = Path(bpy.data.filepath)\n        elif isinstance(filepath, str):\n            filepath = Path(bpy.path.abspath(filepath))\n\n        dirpath = bpathlib.make_absolute(filepath).parent\n        if n \u003e= len(dirpath.parts):\n            all_parts = dirpath.relative_to(dirpath.anchor)\n            return Path(all_parts)\n\n        subset = Path(*dirpath.parts[-n:])\n        return subset\n\n    @staticmethod\n    def abspath(filepath: Union[str, Path]) -\u003e Path:\n        \"\"\"Return the filepath as absolute path.\"\"\"\n\n        # This changes blendfile-relative paths to absolute.\n        # It does not resolve &#96;..&#96; entries, though.\n        abs_unclean = Path(bpy.path.abspath(str(filepath)))\n        abs_clean: Path = bpathlib.make_absolute(abs_unclean)\n        return abs_clean\n\n\n# Mapping from AvailableJobType.setting.type to a callable that converts a value",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 56,
            "text": "class JobTypePropertyGroup:"
          },
          {
            "lineNumber": 173
          },
          {
            "lineNumber": 174
          },
          {
            "lineNumber": 175,
            "text": "    @staticmethod"
          },
          {
            "lineNumber": 176,
            "text": "    def last_n_dir_parts(n: int, filepath: Union[str, Path, None] = None) -\u003e Path:"
          },
          {
            "lineNumber": 177,
            "text": "        \"\"\"Return the last &#96;n&#96; parts of the directory of &#96;filepath&#96;."
          },
          {
            "lineNumber": 178
          },
          {
            "lineNumber": 179,
            "text": "        If &#96;n&#96; is 0, returns an empty &#96;Path()&#96;."
          },
          {
            "lineNumber": 180,
            "text": "        If &#96;filepath&#96; is None, uses bpy.data.filepath instead."
          },
          {
            "lineNumber": 181
          },
          {
            "lineNumber": 182,
            "text": "        \u003e\u003e\u003e str(last_n_dir_parts(2, \"/path/to/some/file.blend\"))"
          },
          {
            "lineNumber": 183,
            "text": "        \"to/some\""
          },
          {
            "lineNumber": 184
          },
          {
            "lineNumber": 185,
            "text": "        Always returns a relative path:"
          },
          {
            "lineNumber": 186,
            "text": "        \u003e\u003e\u003e str(last_n_dir_parts(200, \"C:\\\\path\\\\to\\\\some\\\\file.blend\"))"
          },
          {
            "lineNumber": 187,
            "text": "        \"path\\\\to\\\\some\""
          },
          {
            "lineNumber": 188,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 189
          },
          {
            "lineNumber": 190,
            "text": "        if n \u003c= 0:"
          },
          {
            "lineNumber": 191,
            "text": "            return Path()"
          },
          {
            "lineNumber": 192
          },
          {
            "lineNumber": 193,
            "text": "        if filepath is None:"
          },
          {
            "lineNumber": 194,
            "text": "            filepath = Path(bpy.data.filepath)"
          },
          {
            "lineNumber": 195,
            "text": "        elif isinstance(filepath, str):"
          },
          {
            "lineNumber": 196,
            "text": "            filepath = Path(bpy.path.abspath(filepath))"
          },
          {
            "lineNumber": 197
          },
          {
            "lineNumber": 198,
            "text": "        dirpath = bpathlib.make_absolute(filepath).parent"
          },
          {
            "lineNumber": 199,
            "text": "        if n \u003e= len(dirpath.parts):"
          },
          {
            "lineNumber": 200,
            "text": "            all_parts = dirpath.relative_to(dirpath.anchor)"
          },
          {
            "lineNumber": 201,
            "text": "            return Path(all_parts)"
          },
          {
            "lineNumber": 202
          },
          {
            "lineNumber": 203,
            "text": "        subset = Path(*dirpath.parts[-n:])"
          },
          {
            "lineNumber": 204,
            "text": "        return subset"
          },
          {
            "lineNumber": 205
          },
          {
            "lineNumber": 206,
            "text": "    @staticmethod"
          },
          {
            "lineNumber": 207,
            "text": "    def abspath(filepath: Union[str, Path]) -\u003e Path:"
          },
          {
            "lineNumber": 208,
            "text": "        \"\"\"Return the filepath as absolute path.\"\"\""
          },
          {
            "lineNumber": 209
          },
          {
            "lineNumber": 210,
            "text": "        # This changes blendfile-relative paths to absolute."
          },
          {
            "lineNumber": 211,
            "text": "        # It does not resolve &#96;..&#96; entries, though."
          },
          {
            "lineNumber": 212,
            "text": "        abs_unclean = Path(bpy.path.abspath(str(filepath)))"
          },
          {
            "lineNumber": 213,
            "text": "        abs_clean: Path = bpathlib.make_absolute(abs_unclean)"
          },
          {
            "isSignature": true,
            "lineNumber": 214,
            "text": "        return abs_clean"
          },
          {
            "lineNumber": 215
          },
          {
            "lineNumber": 216
          },
          {
            "lineNumber": 217,
            "text": "# Mapping from AvailableJobType.setting.type to a callable that converts a value"
          }
        ],
        "range": {
          "endPosition": {
            "column": 80,
            "line": 216
          },
          "startPosition": {
            "line": 55
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_types_propgroup.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 57
              },
              "startPosition": {
                "column": 1,
                "line": 56
              }
            }
          ]
        }
      },
      "score": 0.233268603682518
    },
    {
      "codeBlock": {
        "contents": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore\n\n\n    def _create_checkout_definition(self) -\u003e Optional[_ShamanRequirementsRequest]:\n        \"\"\"Create the checkout definition file for this BAT pack.\n\n        :returns: the checkout definition.\n\n        If there was an error and file transfer was aborted, the checkout\n        definition will be empty.\n        \"\"\"\n\n        from ..manager.models import (\n            ShamanRequirementsRequest,\n            ShamanFileSpec,\n        )\n\n        filespecs: list[ShamanFileSpec] = []\n\n        for src, dst, act in self.iter_queue():\n            try:\n                checksum = cache.compute_cached_checksum(src)\n                filesize = src.stat().st_size\n                relpath = str(_root_path_strip(dst))\n\n                filespec = ShamanFileSpec(\n                    sha=checksum,\n                    size=filesize,\n                    path=relpath,\n                )\n                if filespec in filespecs:\n                    # FIXME: there is an issue in BAT that some UDIM files are\n                    # reported twice. There is no use asking Shaman to check\n                    # them out twice, so avoid duplicates here for now.\n                    # ShamanFileSpec is not a hashable type, so unfortunately we\n                    # can't use a set() here.\n                    continue\n                filespecs.append(filespec)\n                self._rel_to_local_path[relpath] = src\n\n                if act == submodules.transfer.Action.MOVE:\n                    self._delete_when_done.append(src)\n            except Exception:\n                # We have to catch exceptions in a broad way, as this is running in",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 99,
            "text": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore"
          },
          {
            "lineNumber": 200
          },
          {
            "lineNumber": 201
          },
          {
            "lineNumber": 202,
            "text": "    def _create_checkout_definition(self) -\u003e Optional[_ShamanRequirementsRequest]:"
          },
          {
            "lineNumber": 203,
            "text": "        \"\"\"Create the checkout definition file for this BAT pack."
          },
          {
            "lineNumber": 204
          },
          {
            "lineNumber": 205,
            "text": "        :returns: the checkout definition."
          },
          {
            "lineNumber": 206
          },
          {
            "lineNumber": 207,
            "text": "        If there was an error and file transfer was aborted, the checkout"
          },
          {
            "lineNumber": 208,
            "text": "        definition will be empty."
          },
          {
            "lineNumber": 209,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 210
          },
          {
            "lineNumber": 211,
            "text": "        from ..manager.models import ("
          },
          {
            "lineNumber": 212,
            "text": "            ShamanRequirementsRequest,"
          },
          {
            "lineNumber": 213,
            "text": "            ShamanFileSpec,"
          },
          {
            "lineNumber": 214,
            "text": "        )"
          },
          {
            "lineNumber": 215
          },
          {
            "lineNumber": 216,
            "text": "        filespecs: list[ShamanFileSpec] = []"
          },
          {
            "lineNumber": 217
          },
          {
            "lineNumber": 218,
            "text": "        for src, dst, act in self.iter_queue():"
          },
          {
            "lineNumber": 219,
            "text": "            try:"
          },
          {
            "lineNumber": 220,
            "text": "                checksum = cache.compute_cached_checksum(src)"
          },
          {
            "lineNumber": 221,
            "text": "                filesize = src.stat().st_size"
          },
          {
            "lineNumber": 222,
            "text": "                relpath = str(_root_path_strip(dst))"
          },
          {
            "lineNumber": 223
          },
          {
            "lineNumber": 224,
            "text": "                filespec = ShamanFileSpec("
          },
          {
            "lineNumber": 225,
            "text": "                    sha=checksum,"
          },
          {
            "lineNumber": 226,
            "text": "                    size=filesize,"
          },
          {
            "lineNumber": 227,
            "text": "                    path=relpath,"
          },
          {
            "lineNumber": 228,
            "text": "                )"
          },
          {
            "lineNumber": 229,
            "text": "                if filespec in filespecs:"
          },
          {
            "lineNumber": 230,
            "text": "                    # FIXME: there is an issue in BAT that some UDIM files are"
          },
          {
            "lineNumber": 231,
            "text": "                    # reported twice. There is no use asking Shaman to check"
          },
          {
            "lineNumber": 232,
            "text": "                    # them out twice, so avoid duplicates here for now."
          },
          {
            "lineNumber": 233,
            "text": "                    # ShamanFileSpec is not a hashable type, so unfortunately we"
          },
          {
            "lineNumber": 234,
            "text": "                    # can't use a set() here."
          },
          {
            "lineNumber": 235,
            "text": "                    continue"
          },
          {
            "lineNumber": 236,
            "text": "                filespecs.append(filespec)"
          },
          {
            "lineNumber": 237,
            "text": "                self._rel_to_local_path[relpath] = src"
          },
          {
            "lineNumber": 238
          },
          {
            "lineNumber": 239,
            "text": "                if act == submodules.transfer.Action.MOVE:"
          },
          {
            "lineNumber": 240,
            "text": "                    self._delete_when_done.append(src)"
          },
          {
            "lineNumber": 241,
            "text": "            except Exception:"
          },
          {
            "lineNumber": 242,
            "text": "                # We have to catch exceptions in a broad way, as this is running in"
          }
        ],
        "range": {
          "endPosition": {
            "line": 473
          },
          "startPosition": {
            "line": 98
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 100
              },
              "startPosition": {
                "column": 1,
                "line": 99
              }
            }
          ]
        }
      },
      "score": 0.22530966997146606
    },
    {
      "codeBlock": {
        "contents": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore\n    \"\"\"Sends files to a Shaman server.\"\"\"\n\n    class AbortUpload(Exception):\n        \"\"\"Raised from the upload callback to abort an upload.\"\"\"\n\n    def __init__(\n        self,\n        api_client: _ApiClient,\n        local_project_root: Path,\n        checkout_path: str,\n    ) -\u003e None:\n        super().__init__()\n        from ..manager.apis import ShamanApi\n\n        self.shaman_api = ShamanApi(api_client)\n\n        self.project_root = local_project_root\n        self.checkout_path = checkout_path\n        self.log = log.getChild(self.__class__.__name__)\n        self.uploaded_files = 0\n        self.uploaded_bytes = 0\n\n        # Mapping from the relative path (as used in Shaman requests) to the\n        # absolute path where we can find the local file. This is typically just\n        # the same as the relative path (relative to the project root), but can\n        # also point to a temporary file when it had to be rewritten.\n        self._rel_to_local_path: dict[str, Path] = {}\n\n        # Temporary files that should be deleted before stopping.\n        self._delete_when_done: list[Path] = []\n\n    # noinspection PyBroadException\n    def run(self) -\u003e None:\n        try:\n            self._run()\n        except Exception as ex:\n            # We have to catch exceptions in a broad way, as this is running in\n            # a separate thread, and exceptions won't otherwise be seen.\n            self.log.exception(\"Error transferring files to Shaman\")\n            self.error_set(\"Unexpected exception transferring files to Shaman: %s\" % ex)\n        finally:\n            # Delete the files that were supposed to be moved.\n            for src in self._delete_when_done:\n                self.delete_file(src)\n            self._delete_when_done.clear()\n\n    def _run(self) -\u003e None:\n        self.uploaded_files = 0",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 99,
            "text": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore"
          },
          {
            "lineNumber": 100,
            "text": "    \"\"\"Sends files to a Shaman server.\"\"\""
          },
          {
            "lineNumber": 101
          },
          {
            "lineNumber": 102,
            "text": "    class AbortUpload(Exception):"
          },
          {
            "lineNumber": 103,
            "text": "        \"\"\"Raised from the upload callback to abort an upload.\"\"\""
          },
          {
            "lineNumber": 104
          },
          {
            "lineNumber": 105,
            "text": "    def __init__("
          },
          {
            "lineNumber": 106,
            "text": "        self,"
          },
          {
            "lineNumber": 107,
            "text": "        api_client: _ApiClient,"
          },
          {
            "lineNumber": 108,
            "text": "        local_project_root: Path,"
          },
          {
            "lineNumber": 109,
            "text": "        checkout_path: str,"
          },
          {
            "lineNumber": 110,
            "text": "    ) -\u003e None:"
          },
          {
            "lineNumber": 111,
            "text": "        super().__init__()"
          },
          {
            "lineNumber": 112,
            "text": "        from ..manager.apis import ShamanApi"
          },
          {
            "lineNumber": 113
          },
          {
            "lineNumber": 114,
            "text": "        self.shaman_api = ShamanApi(api_client)"
          },
          {
            "lineNumber": 115
          },
          {
            "lineNumber": 116,
            "text": "        self.project_root = local_project_root"
          },
          {
            "lineNumber": 117,
            "text": "        self.checkout_path = checkout_path"
          },
          {
            "lineNumber": 118,
            "text": "        self.log = log.getChild(self.__class__.__name__)"
          },
          {
            "lineNumber": 119,
            "text": "        self.uploaded_files = 0"
          },
          {
            "lineNumber": 120,
            "text": "        self.uploaded_bytes = 0"
          },
          {
            "lineNumber": 121
          },
          {
            "lineNumber": 122,
            "text": "        # Mapping from the relative path (as used in Shaman requests) to the"
          },
          {
            "lineNumber": 123,
            "text": "        # absolute path where we can find the local file. This is typically just"
          },
          {
            "lineNumber": 124,
            "text": "        # the same as the relative path (relative to the project root), but can"
          },
          {
            "lineNumber": 125,
            "text": "        # also point to a temporary file when it had to be rewritten."
          },
          {
            "lineNumber": 126,
            "text": "        self._rel_to_local_path: dict[str, Path] = {}"
          },
          {
            "lineNumber": 127
          },
          {
            "lineNumber": 128,
            "text": "        # Temporary files that should be deleted before stopping."
          },
          {
            "lineNumber": 129,
            "text": "        self._delete_when_done: list[Path] = []"
          },
          {
            "lineNumber": 130
          },
          {
            "lineNumber": 131,
            "text": "    # noinspection PyBroadException"
          },
          {
            "lineNumber": 132,
            "text": "    def run(self) -\u003e None:"
          },
          {
            "lineNumber": 133,
            "text": "        try:"
          },
          {
            "lineNumber": 134,
            "text": "            self._run()"
          },
          {
            "lineNumber": 135,
            "text": "        except Exception as ex:"
          },
          {
            "lineNumber": 136,
            "text": "            # We have to catch exceptions in a broad way, as this is running in"
          },
          {
            "lineNumber": 137,
            "text": "            # a separate thread, and exceptions won't otherwise be seen."
          },
          {
            "lineNumber": 138,
            "text": "            self.log.exception(\"Error transferring files to Shaman\")"
          },
          {
            "lineNumber": 139,
            "text": "            self.error_set(\"Unexpected exception transferring files to Shaman: %s\" % ex)"
          },
          {
            "lineNumber": 140,
            "text": "        finally:"
          },
          {
            "lineNumber": 141,
            "text": "            # Delete the files that were supposed to be moved."
          },
          {
            "lineNumber": 142,
            "text": "            for src in self._delete_when_done:"
          },
          {
            "lineNumber": 143,
            "text": "                self.delete_file(src)"
          },
          {
            "lineNumber": 144,
            "text": "            self._delete_when_done.clear()"
          },
          {
            "lineNumber": 145
          },
          {
            "lineNumber": 146,
            "text": "    def _run(self) -\u003e None:"
          },
          {
            "lineNumber": 147,
            "text": "        self.uploaded_files = 0"
          }
        ],
        "range": {
          "endPosition": {
            "line": 473
          },
          "startPosition": {
            "column": 33,
            "line": 95
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.21092869341373444
    },
    {
      "codeBlock": {
        "contents": "def _root_path_strip(path: PurePath) -\u003e PurePosixPath:\n    \"\"\"Strip off the leading / (POSIX) and drive letter (Windows).\n\n    Note that this is limited to paths of the current platform. In other words,\n    a &#96;PurePosixPath('X:/path/to/file')&#96; will be returned as-is, as it's\n    considered relative on a POSIX platform. This is not an issue as this\n    function is just meant to strip off the platform-specific root path returned\n    by &#96;_root_path()&#96;.\n    \"\"\"\n\n    if path.is_absolute():\n        return PurePosixPath(*path.parts[1:])\n    return PurePosixPath(path)\n\n\ndef _encode_original_filename_header(filename: str) -\u003e str:\n    \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 519,
            "text": "def _root_path_strip(path: PurePath) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 520,
            "text": "    \"\"\"Strip off the leading / (POSIX) and drive letter (Windows)."
          },
          {
            "lineNumber": 521
          },
          {
            "lineNumber": 522,
            "text": "    Note that this is limited to paths of the current platform. In other words,"
          },
          {
            "lineNumber": 523,
            "text": "    a &#96;PurePosixPath('X:/path/to/file')&#96; will be returned as-is, as it's"
          },
          {
            "lineNumber": 524,
            "text": "    considered relative on a POSIX platform. This is not an issue as this"
          },
          {
            "lineNumber": 525,
            "text": "    function is just meant to strip off the platform-specific root path returned"
          },
          {
            "lineNumber": 526,
            "text": "    by &#96;_root_path()&#96;."
          },
          {
            "lineNumber": 527,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 528
          },
          {
            "lineNumber": 529,
            "text": "    if path.is_absolute():"
          },
          {
            "lineNumber": 530,
            "text": "        return PurePosixPath(*path.parts[1:])"
          },
          {
            "isSignature": true,
            "lineNumber": 531,
            "text": "    return PurePosixPath(path)"
          },
          {
            "lineNumber": 532
          },
          {
            "lineNumber": 533
          },
          {
            "isSignature": true,
            "lineNumber": 534,
            "text": "def _encode_original_filename_header(filename: str) -\u003e str:"
          },
          {
            "lineNumber": 535,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 543
          },
          "startPosition": {
            "column": 29,
            "line": 515
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.20008283853530884
    },
    {
      "codeBlock": {
        "contents": "def _cache_path(filepath: Path) -\u003e Path:\n    \"\"\"Compute the cache file for the given file path.\"\"\"\n\n    fs_encoding = sys.getfilesystemencoding()\n    filepath = bpathlib.make_absolute(filepath)\n\n    # Reverse the directory, because most variation is in the last bytes.\n    rev_dir = str(filepath.parent)[::-1]\n    encoded_path = filepath.stem + rev_dir + filepath.suffix\n    cache_key = (\n        base64.urlsafe_b64encode(encoded_path.encode(fs_encoding)).decode().rstrip(\"=\")\n    )\n\n    cache_path = CACHE_ROOT / cache_key[:10] / cache_key[10:]\n    return cache_path\n\n\ndef compute_cached_checksum(filepath: Path) -\u003e str:\n    \"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 93,
            "text": "def _cache_path(filepath: Path) -\u003e Path:"
          },
          {
            "lineNumber": 94,
            "text": "    \"\"\"Compute the cache file for the given file path.\"\"\""
          },
          {
            "lineNumber": 95
          },
          {
            "lineNumber": 96,
            "text": "    fs_encoding = sys.getfilesystemencoding()"
          },
          {
            "lineNumber": 97,
            "text": "    filepath = bpathlib.make_absolute(filepath)"
          },
          {
            "lineNumber": 98
          },
          {
            "lineNumber": 99,
            "text": "    # Reverse the directory, because most variation is in the last bytes."
          },
          {
            "lineNumber": 100,
            "text": "    rev_dir = str(filepath.parent)[::-1]"
          },
          {
            "lineNumber": 101,
            "text": "    encoded_path = filepath.stem + rev_dir + filepath.suffix"
          },
          {
            "lineNumber": 102,
            "text": "    cache_key = ("
          },
          {
            "lineNumber": 103,
            "text": "        base64.urlsafe_b64encode(encoded_path.encode(fs_encoding)).decode().rstrip(\"=\")"
          },
          {
            "lineNumber": 104,
            "text": "    )"
          },
          {
            "lineNumber": 105
          },
          {
            "lineNumber": 106,
            "text": "    cache_path = CACHE_ROOT / cache_key[:10] / cache_key[10:]"
          },
          {
            "isSignature": true,
            "lineNumber": 107,
            "text": "    return cache_path"
          },
          {
            "lineNumber": 108
          },
          {
            "lineNumber": 109
          },
          {
            "isSignature": true,
            "lineNumber": 110,
            "text": "def compute_cached_checksum(filepath: Path) -\u003e str:"
          },
          {
            "lineNumber": 111,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 155
          },
          "startPosition": {
            "column": 19,
            "line": 89
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\cache.py",
        "signatures": {}
      },
      "score": 0.1820829063653946
    },
    {
      "codeBlock": {
        "contents": "def _search_path_marker(blendfile: Path, marker_path: str) -\u003e Path:\n    \"\"\"Go up the directory hierarchy until a file or directory 'marker_path' is found.\"\"\"\n\n    blendfile_dir: Path = bpathlib.make_absolute(blendfile).parent\n\n    directory = blendfile_dir\n    while True:\n        marker: Path = directory / marker_path\n        if marker.exists():\n            return directory\n\n        parent = directory.parent\n        if directory == parent:\n            # If a directory is its own parent, we're at the root and cannot go\n            # up further.\n            break\n        directory = parent\n\n    # Could not find the marker, so use the directory containing the blend file.\n    return blendfile_dir\n\n\nFinder = Callable[[Path], Path]\n\n\n@dataclasses.dataclass\nclass FinderInfo:\n    label: str",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 45,
            "text": "def _search_path_marker(blendfile: Path, marker_path: str) -\u003e Path:"
          },
          {
            "lineNumber": 46,
            "text": "    \"\"\"Go up the directory hierarchy until a file or directory 'marker_path' is found.\"\"\""
          },
          {
            "lineNumber": 47
          },
          {
            "lineNumber": 48,
            "text": "    blendfile_dir: Path = bpathlib.make_absolute(blendfile).parent"
          },
          {
            "lineNumber": 49
          },
          {
            "lineNumber": 50,
            "text": "    directory = blendfile_dir"
          },
          {
            "lineNumber": 51,
            "text": "    while True:"
          },
          {
            "lineNumber": 52,
            "text": "        marker: Path = directory / marker_path"
          },
          {
            "lineNumber": 53,
            "text": "        if marker.exists():"
          },
          {
            "lineNumber": 54,
            "text": "            return directory"
          },
          {
            "lineNumber": 55
          },
          {
            "lineNumber": 56,
            "text": "        parent = directory.parent"
          },
          {
            "lineNumber": 57,
            "text": "        if directory == parent:"
          },
          {
            "lineNumber": 58,
            "text": "            # If a directory is its own parent, we're at the root and cannot go"
          },
          {
            "lineNumber": 59,
            "text": "            # up further."
          },
          {
            "lineNumber": 60,
            "text": "            break"
          },
          {
            "lineNumber": 61,
            "text": "        directory = parent"
          },
          {
            "lineNumber": 62
          },
          {
            "lineNumber": 63,
            "text": "    # Could not find the marker, so use the directory containing the blend file."
          },
          {
            "isSignature": true,
            "lineNumber": 64,
            "text": "    return blendfile_dir"
          },
          {
            "lineNumber": 65
          },
          {
            "lineNumber": 66
          },
          {
            "lineNumber": 67,
            "text": "Finder = Callable[[Path], Path]"
          },
          {
            "lineNumber": 68
          },
          {
            "lineNumber": 69
          },
          {
            "lineNumber": 70,
            "text": "@dataclasses.dataclass"
          },
          {
            "isSignature": true,
            "lineNumber": 71,
            "text": "class FinderInfo:"
          },
          {
            "lineNumber": 72,
            "text": "    label: str"
          }
        ],
        "range": {
          "endPosition": {
            "line": 73
          },
          "startPosition": {
            "column": 49,
            "line": 41
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\projects.py",
        "signatures": {}
      },
      "score": 0.18185555934906006
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n\"\"\"BAT packing interface for Flamenco.\"\"\"\n\nfrom dataclasses import dataclass\nfrom pathlib import Path, PurePosixPath\nfrom typing import Optional, Any\nimport logging\nimport queue\nimport threading\nimport typing\n\nfrom . import submodules\n\nlog = logging.getLogger(__name__)\n\n# # For using in other parts of the add-on, so only this file imports BAT.\n# Aborted = pack.Aborted\n# FileTransferError = transfer.FileTransferError\n# parse_shaman_endpoint = shaman.parse_endpoint\n\n\nclass Message:\n    \"\"\"Superclass for message objects queued by the BatProgress class.\"\"\"\n\n\n@dataclass\nclass MsgSetWMAttribute(Message):\n    \"\"\"Set a WindowManager attribute to a value.\"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "\"\"\"BAT packing interface for Flamenco.\"\"\""
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "from dataclasses import dataclass"
          },
          {
            "lineNumber": 5,
            "text": "from pathlib import Path, PurePosixPath"
          },
          {
            "lineNumber": 6,
            "text": "from typing import Optional, Any"
          },
          {
            "lineNumber": 7,
            "text": "import logging"
          },
          {
            "lineNumber": 8,
            "text": "import queue"
          },
          {
            "lineNumber": 9,
            "text": "import threading"
          },
          {
            "lineNumber": 10,
            "text": "import typing"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "from . import submodules"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "# # For using in other parts of the add-on, so only this file imports BAT."
          },
          {
            "lineNumber": 17,
            "text": "# Aborted = pack.Aborted"
          },
          {
            "lineNumber": 18,
            "text": "# FileTransferError = transfer.FileTransferError"
          },
          {
            "lineNumber": 19,
            "text": "# parse_shaman_endpoint = shaman.parse_endpoint"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21
          },
          {
            "isSignature": true,
            "lineNumber": 22,
            "text": "class Message:"
          },
          {
            "isSignature": true,
            "lineNumber": 23,
            "text": "    \"\"\"Superclass for message objects queued by the BatProgress class.\"\"\""
          },
          {
            "lineNumber": 24
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "@dataclass"
          },
          {
            "isSignature": true,
            "lineNumber": 27,
            "text": "class MsgSetWMAttribute(Message):"
          },
          {
            "lineNumber": 28,
            "text": "    \"\"\"Set a WindowManager attribute to a value.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 30
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.16932520270347595
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n\"\"\"BAT interface for sending files to the Manager via the Shaman API.\"\"\"\n\nimport email.header\nimport logging\nimport random\nimport platform\nfrom collections import deque\nfrom pathlib import Path, PurePath, PurePosixPath, PureWindowsPath\nfrom typing import TYPE_CHECKING, Optional, Any, Iterable, Iterator\n\nfrom . import cache, submodules\n\nif TYPE_CHECKING:\n    from ..manager import ApiClient as _ApiClient\n\n    from ..manager.models import (\n        ShamanCheckoutResult as _ShamanCheckoutResult,\n        ShamanRequirementsRequest as _ShamanRequirementsRequest,\n        ShamanFileSpec as _ShamanFileSpec,\n    )\nelse:\n    _ApiClient = object\n    _ShamanCheckoutResult = object\n    _ShamanRequirementsRequest = object\n    _ShamanFileSpec = object\n\nlog = logging.getLogger(__name__)\n\nMAX_DEFERRED_PATHS = 8\nMAX_FAILED_PATHS = 8\n\nHashableShamanFileSpec = tuple[str, int, str]\n\"\"\"Tuple of the 'sha', 'size', and 'path' fields of a ShamanFileSpec.\"\"\"\n\n\n# Mypy doesn't understand that submodules.pack.Packer exists.",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "\"\"\"BAT interface for sending files to the Manager via the Shaman API.\"\"\""
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "import email.header"
          },
          {
            "lineNumber": 5,
            "text": "import logging"
          },
          {
            "lineNumber": 6,
            "text": "import random"
          },
          {
            "lineNumber": 7,
            "text": "import platform"
          },
          {
            "lineNumber": 8,
            "text": "from collections import deque"
          },
          {
            "lineNumber": 9,
            "text": "from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath"
          },
          {
            "lineNumber": 10,
            "text": "from typing import TYPE_CHECKING, Optional, Any, Iterable, Iterator"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "from . import cache, submodules"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 15,
            "text": "    from ..manager import ApiClient as _ApiClient"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "    from ..manager.models import ("
          },
          {
            "lineNumber": 18,
            "text": "        ShamanCheckoutResult as _ShamanCheckoutResult,"
          },
          {
            "lineNumber": 19,
            "text": "        ShamanRequirementsRequest as _ShamanRequirementsRequest,"
          },
          {
            "lineNumber": 20,
            "text": "        ShamanFileSpec as _ShamanFileSpec,"
          },
          {
            "lineNumber": 21,
            "text": "    )"
          },
          {
            "lineNumber": 22,
            "text": "else:"
          },
          {
            "lineNumber": 23,
            "text": "    _ApiClient = object"
          },
          {
            "lineNumber": 24,
            "text": "    _ShamanCheckoutResult = object"
          },
          {
            "lineNumber": 25,
            "text": "    _ShamanRequirementsRequest = object"
          },
          {
            "lineNumber": 26,
            "text": "    _ShamanFileSpec = object"
          },
          {
            "lineNumber": 27
          },
          {
            "lineNumber": 28,
            "text": "log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 29
          },
          {
            "lineNumber": 30,
            "text": "MAX_DEFERRED_PATHS = 8"
          },
          {
            "lineNumber": 31,
            "text": "MAX_FAILED_PATHS = 8"
          },
          {
            "lineNumber": 32
          },
          {
            "lineNumber": 33,
            "text": "HashableShamanFileSpec = tuple[str, int, str]"
          },
          {
            "lineNumber": 34,
            "text": "\"\"\"Tuple of the 'sha', 'size', and 'path' fields of a ShamanFileSpec.\"\"\""
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36
          },
          {
            "lineNumber": 37,
            "text": "# Mypy doesn't understand that submodules.pack.Packer exists."
          }
        ],
        "range": {
          "endPosition": {
            "column": 61,
            "line": 36
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {}
      },
      "score": 0.16435015201568604
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _save_blendfile(self, context):\n\n        finally:\n            # Restore the settings we changed, even after an exception.\n            render.use_file_extension = old_use_file_extension\n            render.use_overwrite = old_use_overwrite\n            render.use_placeholder = old_use_placeholder\n\n            # Only restore if the property exists to begin with:\n            if old_use_all_linked_data_direct is not None:\n                prefs.experimental.use_all_linked_data_direct = (\n                    old_use_all_linked_data_direct\n                )\n\n        return filepath\n\n    def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:\n        \"\"\"Ensure that the files are somewhere in the shared storage.\n\n        Returns True if a packing thread has been started, and False otherwise.\n        \"\"\"\n\n        from .bat import interface as bat_interface\n\n        if bat_interface.is_packing():\n            self.report({\"ERROR\"}, \"Another packing operation is running\")\n            self._quit(context)\n            return False\n\n        manager = self._manager_info(context)\n        if not manager:\n            return False\n\n        if manager.shared_storage.shaman_enabled:",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 300,
            "text": "def _save_blendfile(self, context):"
          },
          {
            "lineNumber": 351
          },
          {
            "lineNumber": 352,
            "text": "        finally:"
          },
          {
            "lineNumber": 353,
            "text": "            # Restore the settings we changed, even after an exception."
          },
          {
            "lineNumber": 354,
            "text": "            render.use_file_extension = old_use_file_extension"
          },
          {
            "lineNumber": 355,
            "text": "            render.use_overwrite = old_use_overwrite"
          },
          {
            "lineNumber": 356,
            "text": "            render.use_placeholder = old_use_placeholder"
          },
          {
            "lineNumber": 357
          },
          {
            "lineNumber": 358,
            "text": "            # Only restore if the property exists to begin with:"
          },
          {
            "lineNumber": 359,
            "text": "            if old_use_all_linked_data_direct is not None:"
          },
          {
            "lineNumber": 360,
            "text": "                prefs.experimental.use_all_linked_data_direct = ("
          },
          {
            "lineNumber": 361,
            "text": "                    old_use_all_linked_data_direct"
          },
          {
            "lineNumber": 362,
            "text": "                )"
          },
          {
            "lineNumber": 363
          },
          {
            "lineNumber": 364,
            "text": "        return filepath"
          },
          {
            "lineNumber": 365
          },
          {
            "lineNumber": 366,
            "text": "    def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 367,
            "text": "        \"\"\"Ensure that the files are somewhere in the shared storage."
          },
          {
            "lineNumber": 368
          },
          {
            "lineNumber": 369,
            "text": "        Returns True if a packing thread has been started, and False otherwise."
          },
          {
            "lineNumber": 370,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 371
          },
          {
            "lineNumber": 372,
            "text": "        from .bat import interface as bat_interface"
          },
          {
            "lineNumber": 373
          },
          {
            "lineNumber": 374,
            "text": "        if bat_interface.is_packing():"
          },
          {
            "lineNumber": 375,
            "text": "            self.report({\"ERROR\"}, \"Another packing operation is running\")"
          },
          {
            "lineNumber": 376,
            "text": "            self._quit(context)"
          },
          {
            "lineNumber": 377,
            "text": "            return False"
          },
          {
            "lineNumber": 378
          },
          {
            "lineNumber": 379,
            "text": "        manager = self._manager_info(context)"
          },
          {
            "lineNumber": 380,
            "text": "        if not manager:"
          },
          {
            "lineNumber": 381,
            "text": "            return False"
          },
          {
            "lineNumber": 382
          },
          {
            "lineNumber": 383,
            "text": "        if manager.shared_storage.shaman_enabled:"
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 301
              },
              "startPosition": {
                "column": 5,
                "line": 300
              }
            }
          ]
        }
      },
      "score": 0.16340553760528564
    },
    {
      "codeBlock": {
        "contents": "from .. import wheels\n\n# Load all the submodules we need from BAT in one go.\n_bat_modules = wheels.load_wheel(\n    \"blender_asset_tracer\",\n    (\"blendfile\", \"pack\", \"pack.progress\", \"pack.transfer\", \"pack.shaman\", \"bpathlib\"),\n)\nbat_toplevel, blendfile, pack, progress, transfer, shaman, bpathlib = _bat_modules",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "from .. import wheels"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "# Load all the submodules we need from BAT in one go."
          },
          {
            "lineNumber": 4,
            "text": "_bat_modules = wheels.load_wheel("
          },
          {
            "lineNumber": 5,
            "text": "    \"blender_asset_tracer\","
          },
          {
            "lineNumber": 6,
            "text": "    (\"blendfile\", \"pack\", \"pack.progress\", \"pack.transfer\", \"pack.shaman\", \"bpathlib\"),"
          },
          {
            "lineNumber": 7,
            "text": ")"
          },
          {
            "lineNumber": 8,
            "text": "bat_toplevel, blendfile, pack, progress, transfer, shaman, bpathlib = _bat_modules"
          }
        ],
        "range": {
          "endPosition": {
            "column": 82,
            "line": 7
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\submodules.py",
        "signatures": {}
      },
      "score": 0.16131730377674103
    },
    {
      "codeBlock": {
        "contents": "def find_files(root: Path) -\u003e typing.Iterable[Path]:\n    \"\"\"Recursively finds files in the given root path.\n\n    Directories are recursed into, and file paths are yielded.\n    Symlinks are yielded if they refer to a regular file.\n    \"\"\"\n    queue = deque([root])\n    while queue:\n        path = queue.popleft()\n\n        # Ignore hidden files/dirs; these can be things like '.svn' or '.git',\n        # which shouldn't be sent to Shaman.\n        if path.name.startswith(\".\"):\n            continue\n\n        if path.is_dir():\n            for child in path.iterdir():\n                queue.append(child)\n            continue\n\n        # Only yield symlinks if they link to (a link to) a normal file.\n        if path.is_symlink():\n            symlinked = path.resolve()\n            if symlinked.is_file():\n                yield path\n            continue\n\n        if path.is_file():\n            yield path\n\n\ndef compute_checksum(filepath: Path) -\u003e str:\n    \"\"\"Compute the SHA256 checksum for the given file.\"\"\"",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 45,
            "text": "def find_files(root: Path) -\u003e typing.Iterable[Path]:"
          },
          {
            "lineNumber": 46,
            "text": "    \"\"\"Recursively finds files in the given root path."
          },
          {
            "lineNumber": 47
          },
          {
            "lineNumber": 48,
            "text": "    Directories are recursed into, and file paths are yielded."
          },
          {
            "lineNumber": 49,
            "text": "    Symlinks are yielded if they refer to a regular file."
          },
          {
            "lineNumber": 50,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 51,
            "text": "    queue = deque([root])"
          },
          {
            "lineNumber": 52,
            "text": "    while queue:"
          },
          {
            "lineNumber": 53,
            "text": "        path = queue.popleft()"
          },
          {
            "lineNumber": 54
          },
          {
            "lineNumber": 55,
            "text": "        # Ignore hidden files/dirs; these can be things like '.svn' or '.git',"
          },
          {
            "lineNumber": 56,
            "text": "        # which shouldn't be sent to Shaman."
          },
          {
            "lineNumber": 57,
            "text": "        if path.name.startswith(\".\"):"
          },
          {
            "lineNumber": 58,
            "text": "            continue"
          },
          {
            "lineNumber": 59
          },
          {
            "lineNumber": 60,
            "text": "        if path.is_dir():"
          },
          {
            "lineNumber": 61,
            "text": "            for child in path.iterdir():"
          },
          {
            "lineNumber": 62,
            "text": "                queue.append(child)"
          },
          {
            "lineNumber": 63,
            "text": "            continue"
          },
          {
            "lineNumber": 64
          },
          {
            "lineNumber": 65,
            "text": "        # Only yield symlinks if they link to (a link to) a normal file."
          },
          {
            "lineNumber": 66,
            "text": "        if path.is_symlink():"
          },
          {
            "lineNumber": 67,
            "text": "            symlinked = path.resolve()"
          },
          {
            "lineNumber": 68,
            "text": "            if symlinked.is_file():"
          },
          {
            "lineNumber": 69,
            "text": "                yield path"
          },
          {
            "lineNumber": 70,
            "text": "            continue"
          },
          {
            "lineNumber": 71
          },
          {
            "lineNumber": 72,
            "text": "        if path.is_file():"
          },
          {
            "isSignature": true,
            "lineNumber": 73,
            "text": "            yield path"
          },
          {
            "lineNumber": 74
          },
          {
            "lineNumber": 75
          },
          {
            "isSignature": true,
            "lineNumber": 76,
            "text": "def compute_checksum(filepath: Path) -\u003e str:"
          },
          {
            "lineNumber": 77,
            "text": "    \"\"\"Compute the SHA256 checksum for the given file.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 89
          },
          "startPosition": {
            "column": 33,
            "line": 41
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\cache.py",
        "signatures": {}
      },
      "score": 0.16085819900035858
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:\n\n\n        if manager.shared_storage.shaman_enabled:\n            # self.blendfile_on_farm will be set when BAT created the checkout,\n            # see _on_bat_pack_msg() below.\n            self.blendfile_on_farm = None\n            self._bat_pack_shaman(context, blendfile)\n        elif job_submission.is_file_inside_job_storage(context, blendfile):\n            self.log.info(\n                \"File is already in job storage location, submitting it as-is\"\n            )\n            self._use_blendfile_directly(context, blendfile)\n        else:\n            self.log.info(\n                \"File is not already in job storage location, copying it there\"\n            )\n            try:\n                self.blendfile_on_farm = self._bat_pack_filesystem(context, blendfile)\n            except FileNotFoundError:\n                self._quit(context)\n                return False\n\n        wm = context.window_manager\n        self.timer = wm.event_timer_add(self.TIMER_PERIOD, window=context.window)\n\n        return True\n\n    def _bat_pack_filesystem(\n        self, context: bpy.types.Context, blendfile: Path\n    ) -\u003e PurePosixPath:\n        ",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 366,
            "text": "def _submit_files(self, context: bpy.types.Context, blendfile: Path) -\u003e bool:"
          },
          {
            "lineNumber": 381
          },
          {
            "lineNumber": 382
          },
          {
            "lineNumber": 383,
            "text": "        if manager.shared_storage.shaman_enabled:"
          },
          {
            "lineNumber": 384,
            "text": "            # self.blendfile_on_farm will be set when BAT created the checkout,"
          },
          {
            "lineNumber": 385,
            "text": "            # see _on_bat_pack_msg() below."
          },
          {
            "lineNumber": 386,
            "text": "            self.blendfile_on_farm = None"
          },
          {
            "lineNumber": 387,
            "text": "            self._bat_pack_shaman(context, blendfile)"
          },
          {
            "lineNumber": 388,
            "text": "        elif job_submission.is_file_inside_job_storage(context, blendfile):"
          },
          {
            "lineNumber": 389,
            "text": "            self.log.info("
          },
          {
            "lineNumber": 390,
            "text": "                \"File is already in job storage location, submitting it as-is\""
          },
          {
            "lineNumber": 391,
            "text": "            )"
          },
          {
            "lineNumber": 392,
            "text": "            self._use_blendfile_directly(context, blendfile)"
          },
          {
            "lineNumber": 393,
            "text": "        else:"
          },
          {
            "lineNumber": 394,
            "text": "            self.log.info("
          },
          {
            "lineNumber": 395,
            "text": "                \"File is not already in job storage location, copying it there\""
          },
          {
            "lineNumber": 396,
            "text": "            )"
          },
          {
            "lineNumber": 397,
            "text": "            try:"
          },
          {
            "lineNumber": 398,
            "text": "                self.blendfile_on_farm = self._bat_pack_filesystem(context, blendfile)"
          },
          {
            "lineNumber": 399,
            "text": "            except FileNotFoundError:"
          },
          {
            "lineNumber": 400,
            "text": "                self._quit(context)"
          },
          {
            "lineNumber": 401,
            "text": "                return False"
          },
          {
            "lineNumber": 402
          },
          {
            "lineNumber": 403,
            "text": "        wm = context.window_manager"
          },
          {
            "lineNumber": 404,
            "text": "        self.timer = wm.event_timer_add(self.TIMER_PERIOD, window=context.window)"
          },
          {
            "lineNumber": 405
          },
          {
            "lineNumber": 406,
            "text": "        return True"
          },
          {
            "lineNumber": 407
          },
          {
            "lineNumber": 408,
            "text": "    def _bat_pack_filesystem("
          },
          {
            "lineNumber": 409,
            "text": "        self, context: bpy.types.Context, blendfile: Path"
          },
          {
            "lineNumber": 410,
            "text": "    ) -\u003e PurePosixPath:"
          },
          {
            "lineNumber": 411,
            "text": "        "
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 301
              },
              "startPosition": {
                "column": 5,
                "line": 300
              }
            }
          ]
        }
      },
      "score": 0.1584160327911377
    },
    {
      "codeBlock": {
        "contents": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore\ndef _upload_files(\n        self, to_upload: deque[_ShamanFileSpec]\n    ) -\u003e set[HashableShamanFileSpec]:\n\n        while to_upload:\n            # After too many failures, just retry to get a fresh set of files to upload.\n            if len(failed_specs) \u003e MAX_FAILED_PATHS:\n                self.log.info(\"Too many failures, going to abort this iteration\")\n                failed_specs.update(make_file_specs_hashable_gen(to_upload))\n                return failed_specs\n\n            file_spec = to_upload.popleft()\n            self.log.info(\"   %s\", file_spec.path)\n\n            # Pre-flight check. The generated API code will load the entire file into\n            # memory before sending it to the Shaman. It's faster to do a check at\n            # Shaman first, to see if we need uploading at all.\n            check_resp = self.shaman_api.shaman_file_store_check(\n                checksum=file_spec.sha,\n                filesize=file_spec.size,\n            )\n            if check_resp.status.value == \"stored\":\n                self.log.info(\"  %s: skipping, already on server\", file_spec.path)\n                continue\n\n            # Let the Shaman know whether we can defer uploading this file or not.\n            hashable_file_spec = make_file_spec_hashable(file_spec)\n            can_defer = bool(\n                len(deferred_specs) \u003c MAX_DEFERRED_PATHS\n                and hashable_file_spec not in deferred_specs\n                and len(to_upload)\n            )\n\n            local_filepath = self._rel_to_local_path[file_spec.path]\n            filename_header = _encode_original_filename_header(file_spec.path)\n            try:\n                with local_filepath.open(\"rb\") as file_reader",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 99,
            "text": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore"
          },
          {
            "lineNumber": 303,
            "text": "def _upload_files("
          },
          {
            "lineNumber": 304,
            "text": "        self, to_upload: deque[_ShamanFileSpec]"
          },
          {
            "lineNumber": 305,
            "text": "    ) -\u003e set[HashableShamanFileSpec]:"
          },
          {
            "lineNumber": 339
          },
          {
            "lineNumber": 340,
            "text": "        while to_upload:"
          },
          {
            "lineNumber": 341,
            "text": "            # After too many failures, just retry to get a fresh set of files to upload."
          },
          {
            "lineNumber": 342,
            "text": "            if len(failed_specs) \u003e MAX_FAILED_PATHS:"
          },
          {
            "lineNumber": 343,
            "text": "                self.log.info(\"Too many failures, going to abort this iteration\")"
          },
          {
            "lineNumber": 344,
            "text": "                failed_specs.update(make_file_specs_hashable_gen(to_upload))"
          },
          {
            "lineNumber": 345,
            "text": "                return failed_specs"
          },
          {
            "lineNumber": 346
          },
          {
            "lineNumber": 347,
            "text": "            file_spec = to_upload.popleft()"
          },
          {
            "lineNumber": 348,
            "text": "            self.log.info(\"   %s\", file_spec.path)"
          },
          {
            "lineNumber": 349
          },
          {
            "lineNumber": 350,
            "text": "            # Pre-flight check. The generated API code will load the entire file into"
          },
          {
            "lineNumber": 351,
            "text": "            # memory before sending it to the Shaman. It's faster to do a check at"
          },
          {
            "lineNumber": 352,
            "text": "            # Shaman first, to see if we need uploading at all."
          },
          {
            "lineNumber": 353,
            "text": "            check_resp = self.shaman_api.shaman_file_store_check("
          },
          {
            "lineNumber": 354,
            "text": "                checksum=file_spec.sha,"
          },
          {
            "lineNumber": 355,
            "text": "                filesize=file_spec.size,"
          },
          {
            "lineNumber": 356,
            "text": "            )"
          },
          {
            "lineNumber": 357,
            "text": "            if check_resp.status.value == \"stored\":"
          },
          {
            "lineNumber": 358,
            "text": "                self.log.info(\"  %s: skipping, already on server\", file_spec.path)"
          },
          {
            "lineNumber": 359,
            "text": "                continue"
          },
          {
            "lineNumber": 360
          },
          {
            "lineNumber": 361,
            "text": "            # Let the Shaman know whether we can defer uploading this file or not."
          },
          {
            "lineNumber": 362,
            "text": "            hashable_file_spec = make_file_spec_hashable(file_spec)"
          },
          {
            "lineNumber": 363,
            "text": "            can_defer = bool("
          },
          {
            "lineNumber": 364,
            "text": "                len(deferred_specs) \u003c MAX_DEFERRED_PATHS"
          },
          {
            "lineNumber": 365,
            "text": "                and hashable_file_spec not in deferred_specs"
          },
          {
            "lineNumber": 366,
            "text": "                and len(to_upload)"
          },
          {
            "lineNumber": 367,
            "text": "            )"
          },
          {
            "lineNumber": 368
          },
          {
            "lineNumber": 369,
            "text": "            local_filepath = self._rel_to_local_path[file_spec.path]"
          },
          {
            "lineNumber": 370,
            "text": "            filename_header = _encode_original_filename_header(file_spec.path)"
          },
          {
            "lineNumber": 371,
            "text": "            try:"
          },
          {
            "lineNumber": 372,
            "text": "                with local_filepath.open(\"rb\") as file_reader"
          }
        ],
        "range": {
          "endPosition": {
            "line": 473
          },
          "startPosition": {
            "line": 98
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 100
              },
              "startPosition": {
                "column": 1,
                "line": 99
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 306
              },
              "startPosition": {
                "column": 5,
                "line": 303
              }
            }
          ]
        }
      },
      "score": 0.15559285879135132
    },
    {
      "codeBlock": {
        "contents": "@dataclass\nclass MsgDone(Message):\n    output_path: Path\n    \"\"\"Shaman checkout path, i.e. the root of the job files, relative to the Shaman checkout root.\"\"\"\n    missing_files: list[Path]\n    \"\"\"Path of the submitted blend file, relative to the Shaman checkout root.\"\"\"\n    actual_checkout_path: Optional[PurePosixPath] = None\n\n\n# MyPy doesn't understand the way BAT subpackages are imported.\nclass BatProgress(submodules.progress.Callback):  # type: ignore\n    \"\"\"Report progress of BAT Packing to the given queue.\"\"\"",
        "detailedLines": [
          {
            "lineNumber": 48,
            "text": "@dataclass"
          },
          {
            "isSignature": true,
            "lineNumber": 49,
            "text": "class MsgDone(Message):"
          },
          {
            "lineNumber": 50,
            "text": "    output_path: Path"
          },
          {
            "lineNumber": 51,
            "text": "    \"\"\"Shaman checkout path, i.e. the root of the job files, relative to the Shaman checkout root.\"\"\""
          },
          {
            "lineNumber": 52,
            "text": "    missing_files: list[Path]"
          },
          {
            "lineNumber": 53,
            "text": "    \"\"\"Path of the submitted blend file, relative to the Shaman checkout root.\"\"\""
          },
          {
            "isSignature": true,
            "lineNumber": 54,
            "text": "    actual_checkout_path: Optional[PurePosixPath] = None"
          },
          {
            "lineNumber": 55
          },
          {
            "lineNumber": 56
          },
          {
            "lineNumber": 57,
            "text": "# MyPy doesn't understand the way BAT subpackages are imported."
          },
          {
            "isSignature": true,
            "lineNumber": 58,
            "text": "class BatProgress(submodules.progress.Callback):  # type: ignore"
          },
          {
            "lineNumber": 59,
            "text": "    \"\"\"Report progress of BAT Packing to the given queue.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 126
          },
          "startPosition": {
            "column": 30,
            "line": 44
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.1519242525100708
    },
    {
      "codeBlock": {
        "contents": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore\ndef _upload_files(\n        self, to_upload: deque[_ShamanFileSpec]\n    ) -\u003e set[HashableShamanFileSpec]:\n\n\n        if failed_specs:\n            self.log.info(\n                \"Uploaded %d bytes in %d files so far\",\n                self.uploaded_bytes,\n                self.uploaded_files,\n            )\n            return failed_specs\n\n        self.log.info(\n            \"Done uploading %d bytes in %d files\",\n            self.uploaded_bytes,\n            self.uploaded_files,\n        )\n        return set()\n\n    def report_transferred(self, bytes_transferred: int) -\u003e None:\n        if self._abort.is_set():\n            self.log.warning(\"Interrupting ongoing upload\")\n            raise self.AbortUpload(\"interrupting ongoing upload\")\n        super().report_transferred(bytes_transferred)\n\n    def _request_checkout(\n        self, shaman_file_specs: _ShamanRequirementsRequest\n    ) -\u003e Optional[_ShamanCheckoutResult]:\n        \"\"\"Ask the Shaman to create a checkout of this BAT pack.\"\"\"\n\n        if not self.checkout_path:\n            self.log.warning(\"NOT requesting checkout at Shaman\")\n            return None\n\n        from ..manager.models import ShamanCheckout, ShamanCheckoutResult\n        from ..manager.exceptions import ApiException\n\n        self.log.info(\n            \"Requesting checkout at Shaman for checkout_path=%s\", self.checkout_path\n        )\n\n        checkoutRequest = ShamanCheckout(\n            files=shaman_file_specs.files,\n            checkout_path=str(self.checkout_path),\n        )\n\n        try:\n            result: ShamanCheckoutResult =",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 99,
            "text": "class Transferrer(submodules.transfer.FileTransferer):  # type: ignore"
          },
          {
            "lineNumber": 303,
            "text": "def _upload_files("
          },
          {
            "lineNumber": 304,
            "text": "        self, to_upload: deque[_ShamanFileSpec]"
          },
          {
            "lineNumber": 305,
            "text": "    ) -\u003e set[HashableShamanFileSpec]:"
          },
          {
            "lineNumber": 411
          },
          {
            "lineNumber": 412
          },
          {
            "lineNumber": 413,
            "text": "        if failed_specs:"
          },
          {
            "lineNumber": 414,
            "text": "            self.log.info("
          },
          {
            "lineNumber": 415,
            "text": "                \"Uploaded %d bytes in %d files so far\","
          },
          {
            "lineNumber": 416,
            "text": "                self.uploaded_bytes,"
          },
          {
            "lineNumber": 417,
            "text": "                self.uploaded_files,"
          },
          {
            "lineNumber": 418,
            "text": "            )"
          },
          {
            "lineNumber": 419,
            "text": "            return failed_specs"
          },
          {
            "lineNumber": 420
          },
          {
            "lineNumber": 421,
            "text": "        self.log.info("
          },
          {
            "lineNumber": 422,
            "text": "            \"Done uploading %d bytes in %d files\","
          },
          {
            "lineNumber": 423,
            "text": "            self.uploaded_bytes,"
          },
          {
            "lineNumber": 424,
            "text": "            self.uploaded_files,"
          },
          {
            "lineNumber": 425,
            "text": "        )"
          },
          {
            "lineNumber": 426,
            "text": "        return set()"
          },
          {
            "lineNumber": 427
          },
          {
            "lineNumber": 428,
            "text": "    def report_transferred(self, bytes_transferred: int) -\u003e None:"
          },
          {
            "lineNumber": 429,
            "text": "        if self._abort.is_set():"
          },
          {
            "lineNumber": 430,
            "text": "            self.log.warning(\"Interrupting ongoing upload\")"
          },
          {
            "lineNumber": 431,
            "text": "            raise self.AbortUpload(\"interrupting ongoing upload\")"
          },
          {
            "lineNumber": 432,
            "text": "        super().report_transferred(bytes_transferred)"
          },
          {
            "lineNumber": 433
          },
          {
            "lineNumber": 434,
            "text": "    def _request_checkout("
          },
          {
            "lineNumber": 435,
            "text": "        self, shaman_file_specs: _ShamanRequirementsRequest"
          },
          {
            "lineNumber": 436,
            "text": "    ) -\u003e Optional[_ShamanCheckoutResult]:"
          },
          {
            "lineNumber": 437,
            "text": "        \"\"\"Ask the Shaman to create a checkout of this BAT pack.\"\"\""
          },
          {
            "lineNumber": 438
          },
          {
            "lineNumber": 439,
            "text": "        if not self.checkout_path:"
          },
          {
            "lineNumber": 440,
            "text": "            self.log.warning(\"NOT requesting checkout at Shaman\")"
          },
          {
            "lineNumber": 441,
            "text": "            return None"
          },
          {
            "lineNumber": 442
          },
          {
            "lineNumber": 443,
            "text": "        from ..manager.models import ShamanCheckout, ShamanCheckoutResult"
          },
          {
            "lineNumber": 444,
            "text": "        from ..manager.exceptions import ApiException"
          },
          {
            "lineNumber": 445
          },
          {
            "lineNumber": 446,
            "text": "        self.log.info("
          },
          {
            "lineNumber": 447,
            "text": "            \"Requesting checkout at Shaman for checkout_path=%s\", self.checkout_path"
          },
          {
            "lineNumber": 448,
            "text": "        )"
          },
          {
            "lineNumber": 449
          },
          {
            "lineNumber": 450,
            "text": "        checkoutRequest = ShamanCheckout("
          },
          {
            "lineNumber": 451,
            "text": "            files=shaman_file_specs.files,"
          },
          {
            "lineNumber": 452,
            "text": "            checkout_path=str(self.checkout_path),"
          },
          {
            "lineNumber": 453,
            "text": "        )"
          },
          {
            "lineNumber": 454
          },
          {
            "lineNumber": 455,
            "text": "        try:"
          },
          {
            "lineNumber": 456,
            "text": "            result: ShamanCheckoutResult ="
          }
        ],
        "range": {
          "endPosition": {
            "line": 473
          },
          "startPosition": {
            "line": 98
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\shaman.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 100
              },
              "startPosition": {
                "column": 1,
                "line": 99
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 306
              },
              "startPosition": {
                "column": 5,
                "line": 303
              }
            }
          ]
        }
      },
      "score": 0.1479872465133667
    },
    {
      "codeBlock": {
        "contents": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):\ndef _save_blendfile(self, context):\n\n\n        try:\n            # The file extension should be determined by the render settings, not necessarily\n            # by the settings in the output panel.\n            render.use_file_extension = True\n\n            # Rescheduling should not overwrite existing frames.\n            render.use_overwrite = False\n            render.use_placeholder = False\n\n            # To work around a shortcoming of BAT, ensure that all\n            # indirectly-linked data is still saved as directly-linked.\n            #\n            # See &#96;133dde41bb5b: Improve handling of (in)directly linked status\n            # for linked IDs&#96; in Blender's Git repository.\n            if old_use_all_linked_data_direct is not None:\n                self.log.info(\n                    \"Overriding prefs.experimental.use_all_linked_data_direct = True\"\n                )\n                prefs.experimental.use_all_linked_data_direct = True\n\n            filepath = Path(context.blend_data.filepath)\n            if job_submission.is_file_inside_job_storage(context, filepath):\n                self.log.info(\n                    \"Saving blendfile, already in shared storage: %s\", filepath\n                )\n                bpy.ops.wm.save_as_mainfile()\n            else:\n                filepath = filepath.with_suffix(\".flamenco.blend\")\n                self.log.info(\"Saving copy to temporary file %s\", filepath)\n                bpy.ops.wm.save_as_mainfile(\n                    filepath=str(filepath), compress=True, copy=True\n                )\n            self.temp_blendfile = filepath\n        finally:\n            # Restore the settings we changed, even after an exception.\n            render.use_file_extension = old_use_file_extension",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 102,
            "text": "class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 300,
            "text": "def _save_blendfile(self, context):"
          },
          {
            "lineNumber": 317
          },
          {
            "lineNumber": 318
          },
          {
            "lineNumber": 319,
            "text": "        try:"
          },
          {
            "lineNumber": 320,
            "text": "            # The file extension should be determined by the render settings, not necessarily"
          },
          {
            "lineNumber": 321,
            "text": "            # by the settings in the output panel."
          },
          {
            "lineNumber": 322,
            "text": "            render.use_file_extension = True"
          },
          {
            "lineNumber": 323
          },
          {
            "lineNumber": 324,
            "text": "            # Rescheduling should not overwrite existing frames."
          },
          {
            "lineNumber": 325,
            "text": "            render.use_overwrite = False"
          },
          {
            "lineNumber": 326,
            "text": "            render.use_placeholder = False"
          },
          {
            "lineNumber": 327
          },
          {
            "lineNumber": 328,
            "text": "            # To work around a shortcoming of BAT, ensure that all"
          },
          {
            "lineNumber": 329,
            "text": "            # indirectly-linked data is still saved as directly-linked."
          },
          {
            "lineNumber": 330,
            "text": "            #"
          },
          {
            "lineNumber": 331,
            "text": "            # See &#96;133dde41bb5b: Improve handling of (in)directly linked status"
          },
          {
            "lineNumber": 332,
            "text": "            # for linked IDs&#96; in Blender's Git repository."
          },
          {
            "lineNumber": 333,
            "text": "            if old_use_all_linked_data_direct is not None:"
          },
          {
            "lineNumber": 334,
            "text": "                self.log.info("
          },
          {
            "lineNumber": 335,
            "text": "                    \"Overriding prefs.experimental.use_all_linked_data_direct = True\""
          },
          {
            "lineNumber": 336,
            "text": "                )"
          },
          {
            "lineNumber": 337,
            "text": "                prefs.experimental.use_all_linked_data_direct = True"
          },
          {
            "lineNumber": 338
          },
          {
            "lineNumber": 339,
            "text": "            filepath = Path(context.blend_data.filepath)"
          },
          {
            "lineNumber": 340,
            "text": "            if job_submission.is_file_inside_job_storage(context, filepath):"
          },
          {
            "lineNumber": 341,
            "text": "                self.log.info("
          },
          {
            "lineNumber": 342,
            "text": "                    \"Saving blendfile, already in shared storage: %s\", filepath"
          },
          {
            "lineNumber": 343,
            "text": "                )"
          },
          {
            "lineNumber": 344,
            "text": "                bpy.ops.wm.save_as_mainfile()"
          },
          {
            "lineNumber": 345,
            "text": "            else:"
          },
          {
            "lineNumber": 346,
            "text": "                filepath = filepath.with_suffix(\".flamenco.blend\")"
          },
          {
            "lineNumber": 347,
            "text": "                self.log.info(\"Saving copy to temporary file %s\", filepath)"
          },
          {
            "lineNumber": 348,
            "text": "                bpy.ops.wm.save_as_mainfile("
          },
          {
            "lineNumber": 349,
            "text": "                    filepath=str(filepath), compress=True, copy=True"
          },
          {
            "lineNumber": 350,
            "text": "                )"
          },
          {
            "lineNumber": 351,
            "text": "            self.temp_blendfile = filepath"
          },
          {
            "lineNumber": 352,
            "text": "        finally:"
          },
          {
            "lineNumber": 353,
            "text": "            # Restore the settings we changed, even after an exception."
          },
          {
            "lineNumber": 354,
            "text": "            render.use_file_extension = old_use_file_extension"
          }
        ],
        "range": {
          "endPosition": {
            "line": 652
          },
          "startPosition": {
            "line": 101
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {
          "ranges": [
            {
              "endPosition": {
                "column": 5,
                "line": 103
              },
              "startPosition": {
                "column": 1,
                "line": 102
              }
            },
            {
              "endPosition": {
                "column": 9,
                "line": 301
              },
              "startPosition": {
                "column": 5,
                "line": 300
              }
            }
          ]
        }
      },
      "score": 0.14256319403648376
    }
  ]
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full"><details>
<summary>Tool use: **semantic_search_full**</summary>

</details></tool-use>

_**Agent (default)**_

Changing `relative_only=True` to `False` in both places so BAT uses absolute paths. Updating both methods:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Updating both methods to use absolute paths by changing `relative_only` from `True` to `False`:


_**Agent (default)**_

<tool-use data-tool-type="write" data-tool-name="edit_file_v2">

<details><summary>Tool use: **edit_file_v2** • Edit file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>

</tool-use>

<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
<summary>Tool use: **edit_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="write" data-tool-name="edit_file_v2">

<details><summary>Tool use: **edit_file_v2** • Edit file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>

</tool-use>

<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
<summary>Tool use: **edit_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Verifying the changes and checking for linter errors:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="read_lints">
<details>
<summary>Tool use: **read_lints**</summary>


Parameters:

```json
{
  "paths": [
    "addon/flamenco/operators.py"
  ]
}
```
Result:

```json
{
  "linterErrorsByFile": [
    {
      "relativeWorkspacePath": "addon/flamenco/operators.py"
    }
  ]
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="read_lints"><details>
<summary>Tool use: **read_lints**</summary>

</details></tool-use>

_**Agent (default)**_

Updated the addon to always use absolute paths when sending projects.

## Changes Made

I modified `addon/flamenco/operators.py` in two locations:

1. **`_bat_pack_filesystem` method** (line 445): Changed `relative_only=True` to `relative_only=False`
2. **`_bat_pack_shaman` method** (line 484): Changed `relative_only=True` to `relative_only=False`

Both methods now pass `relative_only=False` to BAT, so it uses absolute paths regardless of whether the original paths were relative or absolute. This should resolve inconsistent file sending across workers.

The changes are complete and there are no linter errors. The addon will now consistently use absolute paths when submitting jobs to Flamenco.

---

_**User**_

Sorry, I didn't realize I forked from main instead of v3.8 release. Are there any conflicts?

---

_**Agent (default)**_

Checking for conflicts between your changes and the v3.8 release. Comparing the current file with the v3.8 version:


_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git status</summary>

```bash
cd e:\flamenco && git status
```

```
On branch flamenco-v3.8-release
Your branch is up to date with 'origin/flamenco-v3.8-release'.

Changes not staged for commit:
  (use "git add &lt;file&gt;..." to update what will be committed)
  (use "git restore &lt;file&gt;..." to discard changes in working directory)
	modified:   addon/flamenco/operators.py

Untracked files:
  (use "git add &lt;file&gt;..." to include in what will be committed)
	.cursorindexingignore
	.specstory/

no changes added to commit (use "git add" and/or "git commit -a")

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git branch --show-current</summary>

```bash
cd e:\flamenco && git branch --show-current
```

```
flamenco-v3.8-release

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git log --oneline --all --grep="v3.8" -n 10</summary>

```bash
cd e:\flamenco && git log --oneline --all --grep="v3.8" -n 10
```

```
9e2ef31d3 Merge remote-tracking branch 'origin/flamenco-v3.8-release'
c5721c421 Mark v3.8 as released today

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Comparing the v3.8 version with main to check for conflicts:


_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git diff HEAD addon/flamenco/operators.py</summary>

```bash
cd e:\flamenco && git diff HEAD addon/flamenco/operators.py
```

```
warning: in the working copy of 'addon/flamenco/operators.py', LF will be replaced by CRLF the next time Git touches it

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git show HEAD:addon/flamenco/operators.py | grep -A 5 -B 5 "relative_only"</summary>

```bash
cd e:\flamenco && git show HEAD:addon/flamenco/operators.py | grep -A 5 -B 5 "relative_only"
```

```
grep: C:\Users\Nathan\AppData\Local\Temp\ps-script-af63c87b-982e-4340-86ba-557e409047fb.ps1:112
Line |
 112 |  . \flamenco &amp;&amp; git show HEAD:addon/flamenco/operators.py | grep -A 5 -B .
     |                                                             ~~~~
     | The term 'grep' is not recognized as a name of a cmdlet, function, script file, or executable program. Check the
     | spelling of the name, or if a path was included, verify that the path is correct and try again.

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git log --oneline origin/main..HEAD -- addon/flamenco/operators.py | head -5</summary>

```bash
cd e:\flamenco && git log --oneline origin/main..HEAD -- addon/flamenco/operators.py | head -5
```

```
head: C:\Users\Nathan\AppData\Local\Temp\ps-script-1f03b1c7-a775-4884-ba56-2b324439e7a9.ps1:112
Line |
 112 |  .  --oneline origin/main..HEAD -- addon/flamenco/operators.py | head -5
     |                                                                  ~~~~
     | The term 'head' is not recognized as a name of a cmdlet, function, script file, or executable program. Check the
     | spelling of the name, or if a path was included, verify that the path is correct and try again.

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git log --oneline HEAD..origin/main -- addon/flamenco/operators.py | head -10</summary>

```bash
cd e:\flamenco && git log --oneline HEAD..origin/main -- addon/flamenco/operators.py | head -10
```

```
head: C:\Users\Nathan\AppData\Local\Temp\ps-script-454c8ad7-e908-4a2f-b1a9-99c0ccf36e62.ps1:112
Line |
 112 |  . --oneline HEAD..origin/main -- addon/flamenco/operators.py | head -10
     |                                                                 ~~~~
     | The term 'head' is not recognized as a name of a cmdlet, function, script file, or executable program. Check the
     | spelling of the name, or if a path was included, verify that the path is correct and try again.

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Using PowerShell-compatible commands:


_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git show HEAD:addon/flamenco/operators.py | Select-String -Pattern "relative_only" -Context 3,3</summary>

```bash
cd e:\flamenco && git show HEAD:addon/flamenco/operators.py | Select-String -Pattern "relative_only" -Context 3,3
```

```

              project=project_path,
              target=str(pack_target_dir),
              exclusion_filter="",  # TODO: get from GUI.
&gt;             relative_only=True,  # TODO: get from GUI.
          )
  
          return PurePosixPath(pack_target_file.as_posix())
              project=project_path,
              target="/",  # Target directory irrelevant for Shaman transfers.
              exclusion_filter="",  # TODO: get from GUI.
&gt;             relative_only=True,  # TODO: get from GUI.
              packer_class=bat_shaman.Packer,
              packer_kwargs=dict(
                  api_client=self.get_api_client(context),

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git log --oneline origin/main..HEAD -- addon/flamenco/operators.py | Select-Object -First 5</summary>

```bash
cd e:\flamenco && git log --oneline origin/main..HEAD -- addon/flamenco/operators.py | Select-Object -First 5
```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git log --oneline HEAD..origin/main -- addon/flamenco/operators.py | Select-Object -First 10</summary>

```bash
cd e:\flamenco && git log --oneline HEAD..origin/main -- addon/flamenco/operators.py | Select-Object -First 10
```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Checking the actual diff to see what changed:


_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git diff addon/flamenco/operators.py</summary>

```bash
cd e:\flamenco && git diff addon/flamenco/operators.py
```

```
warning: in the working copy of 'addon/flamenco/operators.py', LF will be replaced by CRLF the next time Git touches it

```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
<details>
<summary>Tool use: **ripgrep_raw_search**</summary>


Parameters:

```json
{
  "caseInsensitive": false,
  "path": "e:\\flamenco\\addon\\flamenco\\operators.py",
  "pattern": "relative_only"
}
```
Additional data:

```json
{
  "isPruned": true,
  "outputMode": "content",
  "path": "e:\\flamenco\\addon\\flamenco\\operators.py",
  "pattern": "relative_only",
  "topFiles": [
    {
      "matchCount": 2,
      "uri": "addon\\flamenco\\operators.py"
    }
  ],
  "totalFiles": 1,
  "totalMatches": 2
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search"><details>
<summary>Tool use: **ripgrep_raw_search**</summary>

</details></tool-use>

_**Agent (default)**_

Checking differences between main and v3.8 for this file:


_**Agent (default)**_

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2">
<details><summary>Tool use: **run_terminal_command_v2** • Run command: cd e:\flamenco && git diff origin/main origin/flamenco-v3.8-release -- addon/flamenco/operators.py</summary>

```bash
cd e:\flamenco && git diff origin/main origin/flamenco-v3.8-release -- addon/flamenco/operators.py
```
</details>
</tool-use>

<tool-use data-tool-type="shell" data-tool-name="run_terminal_command_v2"><details>
<summary>Tool use: **run_terminal_command_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

No conflicts found. `operators.py` is identical between `main` and `v3.8-release`, so the changes apply cleanly.

The changes are isolated: switching `relative_only=True` to `False` in two places. Since the file is the same in both branches, there are no conflicts.

Noticing the file still shows `relative_only=True`; reapplying the changes:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Reapplying the changes:


_**Agent (default)**_

<tool-use data-tool-type="write" data-tool-name="edit_file_v2">

<details><summary>Tool use: **edit_file_v2** • Edit file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>

</tool-use>

<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
<summary>Tool use: **edit_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="write" data-tool-name="edit_file_v2">

<details><summary>Tool use: **edit_file_v2** • Edit file: e:\flamenco\addon\flamenco\operators.py</summary>

</details>

</tool-use>

<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
<summary>Tool use: **edit_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

## Summary

**No conflicts detected:**
- `operators.py` is identical between `main` and `v3.8-release`
- The changes are isolated (two `True` → `False` switches)
- These changes apply cleanly to both branches

**Changes applied:**
- Line 445: `relative_only=True` → `relative_only=False` in `_bat_pack_filesystem`
- Line 484: `relative_only=True` → `relative_only=False` in `_bat_pack_shaman`

The addon now uses absolute paths when sending projects, which should resolve the inconsistent file sending issue. Since the file is the same in both branches, there are no merge conflicts.

---

_**User**_

Can I use the blender addon vscode extension when the addon's files are two folders in @ \addon\flamenco ?

---

_**Agent (default)**_

Checking the addon structure and any VSCode/Blender addon extension configuration:


_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="list_dir_v2">
<details>
<summary>Tool use: **list_dir_v2**</summary>


Parameters:

```json
{
  "targetDirectory": "e:\\flamenco\\addon"
}
```
Additional data:

```json
{
  "extensionCounts": {
    ".py": 85
  },
  "isPruned": true,
  "rootPath": "e:\\flamenco\\addon",
  "topEntries": [
    {
      "isDir": true,
      "numFiles": 85,
      "path": "e:\\flamenco\\addon\\flamenco"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/.openapi-generator-ignore"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/mypy.ini"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/openapi-generator-cli.jar"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/poetry.lock"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/pyproject.toml"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon/README.md"
    }
  ],
  "totalDirs": 9,
  "totalFiles": 142
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="list_dir_v2"><details>
<summary>Tool use: **list_dir_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="search" data-tool-name="glob_file_search">
<details>
<summary>Tool use: **glob_file_search** • Searched codebase "" • **1** result in **1** directory</summary>

Directory: **** (1 file)

| File |
|------|
| `.vscode/extensions.json` |

</details>
</tool-use>

<tool-use data-tool-type="search" data-tool-name="glob_file_search"><details>
<summary>Tool use: **glob_file_search**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="search" data-tool-name="glob_file_search">
<details>
<summary>Tool use: **glob_file_search** • Searched codebase "" • **0** results in **1** directory</summary>

Directory: **** (0 files)

</details>
</tool-use>

<tool-use data-tool-type="search" data-tool-name="glob_file_search"><details>
<summary>Tool use: **glob_file_search**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\.vscode\extensions.json</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full">
<details>
<summary>Tool use: **semantic_search_full**</summary>


Parameters:

```json
{
  "explanation": "",
  "includePattern": "addon",
  "query": "What is the structure of the Flamenco addon and where are the main Python files located?",
  "topK": 10
}
```
Result:

```json
{
  "codeResults": [
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n\n# \u003cpep8 compliant\u003e\n\nbl_info = {\n    \"name\": \"Flamenco\",\n    \"author\": \"Sybren A. Stüvel\",\n    \"version\": (3, 8, 2),\n    \"blender\": (3, 1, 0),\n    \"description\": \"Flamenco client for Blender.\",\n    \"location\": \"Output Properties \u003e Flamenco\",\n    \"doc_url\": \"https://flamenco.blender.org/\",\n    \"category\": \"System\",\n    \"support\": \"COMMUNITY\",\n    \"warning\": \"\",\n}\n\nfrom pathlib import Path\n\n__is_first_load = \"operators\" not in locals()\nif __is_first_load:\n    from . import (\n        operators,\n        gui,\n        job_types,\n        comms,\n        preferences,\n        projects,\n        worker_tags,\n        manager_info,\n    )\nelse:\n    import importlib\n\n    operators = importlib.reload(operators)\n    gui = importlib.reload(gui)\n    job_types = importlib.reload(job_types)\n    comms = importlib.reload(comms)\n    preferences = importlib.reload(preferences)\n    projects = importlib.reload(projects)\n    worker_tags = importlib.reload(worker_tags)\n    manager_info = importlib.reload(manager_info)\n\nimport bpy\n\n\n@bpy.app.handlers.persistent\ndef discard_global_flamenco_data(_):\n    job_types.discard_flamenco_data()\n    comms.discard_flamenco_data()\n\n    bpy.context.window_manager.flamenco_version_mismatch = False\n\n\ndef redraw(self, context):\n    if context.area is None:\n        return\n    context.area.tag_redraw()",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "# \u003cpep8 compliant\u003e"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "bl_info = {"
          },
          {
            "lineNumber": 6,
            "text": "    \"name\": \"Flamenco\","
          },
          {
            "lineNumber": 7,
            "text": "    \"author\": \"Sybren A. Stüvel\","
          },
          {
            "lineNumber": 8,
            "text": "    \"version\": (3, 8, 2),"
          },
          {
            "lineNumber": 9,
            "text": "    \"blender\": (3, 1, 0),"
          },
          {
            "lineNumber": 10,
            "text": "    \"description\": \"Flamenco client for Blender.\","
          },
          {
            "lineNumber": 11,
            "text": "    \"location\": \"Output Properties \u003e Flamenco\","
          },
          {
            "lineNumber": 12,
            "text": "    \"doc_url\": \"https://flamenco.blender.org/\","
          },
          {
            "lineNumber": 13,
            "text": "    \"category\": \"System\","
          },
          {
            "lineNumber": 14,
            "text": "    \"support\": \"COMMUNITY\","
          },
          {
            "lineNumber": 15,
            "text": "    \"warning\": \"\","
          },
          {
            "lineNumber": 16,
            "text": "}"
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "from pathlib import Path"
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20,
            "text": "__is_first_load = \"operators\" not in locals()"
          },
          {
            "lineNumber": 21,
            "text": "if __is_first_load:"
          },
          {
            "lineNumber": 22,
            "text": "    from . import ("
          },
          {
            "lineNumber": 23,
            "text": "        operators,"
          },
          {
            "lineNumber": 24,
            "text": "        gui,"
          },
          {
            "lineNumber": 25,
            "text": "        job_types,"
          },
          {
            "lineNumber": 26,
            "text": "        comms,"
          },
          {
            "lineNumber": 27,
            "text": "        preferences,"
          },
          {
            "lineNumber": 28,
            "text": "        projects,"
          },
          {
            "lineNumber": 29,
            "text": "        worker_tags,"
          },
          {
            "lineNumber": 30,
            "text": "        manager_info,"
          },
          {
            "lineNumber": 31,
            "text": "    )"
          },
          {
            "lineNumber": 32,
            "text": "else:"
          },
          {
            "lineNumber": 33,
            "text": "    import importlib"
          },
          {
            "lineNumber": 34
          },
          {
            "lineNumber": 35,
            "text": "    operators = importlib.reload(operators)"
          },
          {
            "lineNumber": 36,
            "text": "    gui = importlib.reload(gui)"
          },
          {
            "lineNumber": 37,
            "text": "    job_types = importlib.reload(job_types)"
          },
          {
            "lineNumber": 38,
            "text": "    comms = importlib.reload(comms)"
          },
          {
            "lineNumber": 39,
            "text": "    preferences = importlib.reload(preferences)"
          },
          {
            "lineNumber": 40,
            "text": "    projects = importlib.reload(projects)"
          },
          {
            "lineNumber": 41,
            "text": "    worker_tags = importlib.reload(worker_tags)"
          },
          {
            "lineNumber": 42,
            "text": "    manager_info = importlib.reload(manager_info)"
          },
          {
            "lineNumber": 43
          },
          {
            "lineNumber": 44,
            "text": "import bpy"
          },
          {
            "lineNumber": 45
          },
          {
            "lineNumber": 46
          },
          {
            "lineNumber": 47,
            "text": "@bpy.app.handlers.persistent"
          },
          {
            "isSignature": true,
            "lineNumber": 48,
            "text": "def discard_global_flamenco_data(_):"
          },
          {
            "lineNumber": 49,
            "text": "    job_types.discard_flamenco_data()"
          },
          {
            "lineNumber": 50,
            "text": "    comms.discard_flamenco_data()"
          },
          {
            "lineNumber": 51
          },
          {
            "isSignature": true,
            "lineNumber": 52,
            "text": "    bpy.context.window_manager.flamenco_version_mismatch = False"
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54
          },
          {
            "isSignature": true,
            "lineNumber": 55,
            "text": "def redraw(self, context):"
          },
          {
            "lineNumber": 56,
            "text": "    if context.area is None:"
          },
          {
            "lineNumber": 57,
            "text": "        return"
          },
          {
            "isSignature": true,
            "lineNumber": 58,
            "text": "    context.area.tag_redraw()"
          }
        ],
        "range": {
          "endPosition": {
            "column": 29,
            "line": 57
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\__init__.py",
        "signatures": {}
      },
      "score": 0.4760012924671173
    },
    {
      "codeBlock": {
        "contents": "# Flamenco Blender add-on\n\n## Setting up development environment\n\n&#96;&#96;&#96;\n~/workspace/blender-git/build_linux/bin/3.1/python/bin/python3.9 -m venv --upgrade-deps venv\n. ./venv/bin/activate\npip install poetry\npoetry install\n&#96;&#96;&#96;\n\n## Generating the OpenAPI client\n\n1. Make sure Java is installed (so &#96;java --version&#96; shows something sensible).\n2. In the root directory of the repository, run &#96;make generate-py&#96;\n\n\n## Type annotations and lazy imports\n\nThis add-on tries to only load Python packages from wheel files when necessary. Loading things from wheels is tricky, as they basically pollute the &#96;sys.modules&#96; dictionary and thus can \"leak\" to other add-ons. This can cause conflicts when, for example, another add-on is using a different version of the same package.\n\nThe result is that sometimes there are some strange hoops to jump through. The most obvious one is for type annotations. This is why you'll see code like:\n\n&#96;&#96;&#96;\nif TYPE_CHECKING:\n    from .bat_interface import _PackThread\nelse:\n    _PackThread = object\n&#96;&#96;&#96;\n\nThis makes it possible to declare a function with &#96;def func() -\u003e _PackThread&#96;, without having to load &#96;bat_interface&#96; immediately at import time.",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# Flamenco Blender add-on"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "## Setting up development environment"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 6,
            "text": "~/workspace/blender-git/build_linux/bin/3.1/python/bin/python3.9 -m venv --upgrade-deps venv"
          },
          {
            "lineNumber": 7,
            "text": ". ./venv/bin/activate"
          },
          {
            "lineNumber": 8,
            "text": "pip install poetry"
          },
          {
            "lineNumber": 9,
            "text": "poetry install"
          },
          {
            "lineNumber": 10,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "## Generating the OpenAPI client"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "1. Make sure Java is installed (so &#96;java --version&#96; shows something sensible)."
          },
          {
            "lineNumber": 15,
            "text": "2. In the root directory of the repository, run &#96;make generate-py&#96;"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "## Type annotations and lazy imports"
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20,
            "text": "This add-on tries to only load Python packages from wheel files when necessary. Loading things from wheels is tricky, as they basically pollute the &#96;sys.modules&#96; dictionary and thus can \"leak\" to other add-ons. This can cause conflicts when, for example, another add-on is using a different version of the same package."
          },
          {
            "lineNumber": 21
          },
          {
            "lineNumber": 22,
            "text": "The result is that sometimes there are some strange hoops to jump through. The most obvious one is for type annotations. This is why you'll see code like:"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 25,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 26,
            "text": "    from .bat_interface import _PackThread"
          },
          {
            "lineNumber": 27,
            "text": "else:"
          },
          {
            "lineNumber": 28,
            "text": "    _PackThread = object"
          },
          {
            "lineNumber": 29,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "This makes it possible to declare a function with &#96;def func() -\u003e _PackThread&#96;, without having to load &#96;bat_interface&#96; immediately at import time."
          }
        ],
        "range": {
          "endPosition": {
            "line": 31
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\README.md",
        "signatures": {}
      },
      "score": 0.4320353865623474
    },
    {
      "codeBlock": {
        "contents": "# Flamenco\nRender Farm manager API\n\nThe &#96;flamenco.manager&#96; package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:\n\n- API version: 1.0.0\n- Package version: 3.8.2\n- Build package: org.openapitools.codegen.languages.PythonClientCodegen\nFor more information, please visit [https://flamenco.blender.org/](https://flamenco.blender.org/)\n\n## Requirements.\n\nPython \u003e=3.6\n\n## Installation \u0026 Usage\n\nThis python library package is generated without supporting files like setup.py or requirements files\n\nTo be able to use it, you will need these dependencies in your own package that uses this library:\n\n* urllib3 \u003e= 1.25.3\n* python-dateutil\n\n## Getting Started\n\nIn your own code, to use this library to connect and interact with Flamenco,\nyou can run the following:\n\n&#96;&#96;&#96;python\n\nimport time\nimport flamenco.manager\nfrom pprint import pprint\nfrom flamenco.manager.api import jobs_api\nfrom flamenco.manager.model.available_job_type import AvailableJobType\nfrom flamenco.manager.model.available_job_types import AvailableJobTypes\nfrom flamenco.manager.model.error import Error\nfrom flamenco.manager.model.job import Job\nfrom flamenco.manager.model.job_blocklist import JobBlocklist\nfrom flamenco.manager.model.job_deletion_info import JobDeletionInfo\nfrom flamenco.manager.model.job_last_rendered_image_info import JobLastRenderedImageInfo\nfrom flamenco.manager.model.job_mass_deletion_selection import JobMassDeletionSelection\nfrom flamenco.manager.model.job_priority_change import JobPriorityChange\nfrom flamenco.manager.model.job_status_change import JobStatusChange\nfrom flamenco.manager.model.job_tag_change import JobTagChange\nfrom flamenco.manager.model.job_tasks_summary import JobTasksSummary\nfrom flamenco.manager.model.jobs_query_result import JobsQueryResult\nfrom flamenco.manager.model.submitted_job import SubmittedJob\nfrom flamenco.manager.model.task import Task\nfrom flamenco.manager.model.task_log_info import TaskLogInfo\nfrom flamenco.manager.model.task_status_change import TaskStatusChange",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# Flamenco"
          },
          {
            "lineNumber": 2,
            "text": "Render Farm manager API"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "The &#96;flamenco.manager&#96; package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "- API version: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "- Package version: 3.8.2"
          },
          {
            "lineNumber": 8,
            "text": "- Build package: org.openapitools.codegen.languages.PythonClientCodegen"
          },
          {
            "lineNumber": 9,
            "text": "For more information, please visit [https://flamenco.blender.org/](https://flamenco.blender.org/)"
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "## Requirements."
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "Python \u003e=3.6"
          },
          {
            "lineNumber": 14
          },
          {
            "lineNumber": 15,
            "text": "## Installation \u0026 Usage"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "This python library package is generated without supporting files like setup.py or requirements files"
          },
          {
            "lineNumber": 18
          },
          {
            "lineNumber": 19,
            "text": "To be able to use it, you will need these dependencies in your own package that uses this library:"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21,
            "text": "* urllib3 \u003e= 1.25.3"
          },
          {
            "lineNumber": 22,
            "text": "* python-dateutil"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "## Getting Started"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "In your own code, to use this library to connect and interact with Flamenco,"
          },
          {
            "lineNumber": 27,
            "text": "you can run the following:"
          },
          {
            "lineNumber": 28
          },
          {
            "lineNumber": 29,
            "text": "&#96;&#96;&#96;python"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "import time"
          },
          {
            "lineNumber": 32,
            "text": "import flamenco.manager"
          },
          {
            "lineNumber": 33,
            "text": "from pprint import pprint"
          },
          {
            "lineNumber": 34,
            "text": "from flamenco.manager.api import jobs_api"
          },
          {
            "lineNumber": 35,
            "text": "from flamenco.manager.model.available_job_type import AvailableJobType"
          },
          {
            "lineNumber": 36,
            "text": "from flamenco.manager.model.available_job_types import AvailableJobTypes"
          },
          {
            "lineNumber": 37,
            "text": "from flamenco.manager.model.error import Error"
          },
          {
            "lineNumber": 38,
            "text": "from flamenco.manager.model.job import Job"
          },
          {
            "lineNumber": 39,
            "text": "from flamenco.manager.model.job_blocklist import JobBlocklist"
          },
          {
            "lineNumber": 40,
            "text": "from flamenco.manager.model.job_deletion_info import JobDeletionInfo"
          },
          {
            "lineNumber": 41,
            "text": "from flamenco.manager.model.job_last_rendered_image_info import JobLastRenderedImageInfo"
          },
          {
            "lineNumber": 42,
            "text": "from flamenco.manager.model.job_mass_deletion_selection import JobMassDeletionSelection"
          },
          {
            "lineNumber": 43,
            "text": "from flamenco.manager.model.job_priority_change import JobPriorityChange"
          },
          {
            "lineNumber": 44,
            "text": "from flamenco.manager.model.job_status_change import JobStatusChange"
          },
          {
            "lineNumber": 45,
            "text": "from flamenco.manager.model.job_tag_change import JobTagChange"
          },
          {
            "lineNumber": 46,
            "text": "from flamenco.manager.model.job_tasks_summary import JobTasksSummary"
          },
          {
            "lineNumber": 47,
            "text": "from flamenco.manager.model.jobs_query_result import JobsQueryResult"
          },
          {
            "lineNumber": 48,
            "text": "from flamenco.manager.model.submitted_job import SubmittedJob"
          },
          {
            "lineNumber": 49,
            "text": "from flamenco.manager.model.task import Task"
          },
          {
            "lineNumber": 50,
            "text": "from flamenco.manager.model.task_log_info import TaskLogInfo"
          },
          {
            "lineNumber": 51,
            "text": "from flamenco.manager.model.task_status_change import TaskStatusChange"
          }
        ],
        "range": {
          "endPosition": {
            "line": 51
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager_README.md",
        "signatures": {}
      },
      "score": 0.3994934856891632
    },
    {
      "codeBlock": {
        "contents": "[tool.poetry]\nname = \"flamenco\"\nversion = \"3.0\"\ndescription = \"Flamenco client add-on for Blender\"\nauthors = [\"Sybren A. Stüvel \u003csybren@blender.org\u003e\"]\nlicense = \"GPL-3.0-or-later\"\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\nrequests = \"^2.27.1\"\nopenapi-core = \"^0.14.2\"\npython-dateutil = \"^2.8.2\"\n\n[tool.poetry.dev-dependencies]\nmypy = \"^0.941\"\nblack = \"^22.1.0\"\n\n[build-system]\nrequires = [\"poetry\u003e=0.12\"]\nbuild-backend = \"poetry.masonry.api\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "[tool.poetry]"
          },
          {
            "lineNumber": 2,
            "text": "name = \"flamenco\""
          },
          {
            "lineNumber": 3,
            "text": "version = \"3.0\""
          },
          {
            "lineNumber": 4,
            "text": "description = \"Flamenco client add-on for Blender\""
          },
          {
            "lineNumber": 5,
            "text": "authors = [\"Sybren A. Stüvel \u003csybren@blender.org\u003e\"]"
          },
          {
            "lineNumber": 6,
            "text": "license = \"GPL-3.0-or-later\""
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "[tool.poetry.dependencies]"
          },
          {
            "lineNumber": 9,
            "text": "python = \"^3.10\""
          },
          {
            "lineNumber": 10,
            "text": "requests = \"^2.27.1\""
          },
          {
            "lineNumber": 11,
            "text": "openapi-core = \"^0.14.2\""
          },
          {
            "lineNumber": 12,
            "text": "python-dateutil = \"^2.8.2\""
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "[tool.poetry.dev-dependencies]"
          },
          {
            "lineNumber": 15,
            "text": "mypy = \"^0.941\""
          },
          {
            "lineNumber": 16,
            "text": "black = \"^22.1.0\""
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "[build-system]"
          },
          {
            "lineNumber": 19,
            "text": "requires = [\"poetry\u003e=0.12\"]"
          },
          {
            "lineNumber": 20,
            "text": "build-backend = \"poetry.masonry.api\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 20
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\pyproject.toml",
        "signatures": {}
      },
      "score": 0.38450324535369873
    },
    {
      "codeBlock": {
        "contents": "# flake8: noqa\n\n# import all models into this package\n# if you have many models here with many references from one model to another this may\n# raise a RecursionError\n# to avoid this, import only the models that you directly need like:\n# from from flamenco.manager.model.pet import Pet\n# or import this package, but before doing it, use:\n# import sys\n# sys.setrecursionlimit(n)\n\nfrom flamenco.manager.model.assigned_task import AssignedTask\nfrom flamenco.manager.model.assigned_worker import AssignedWorker\nfrom flamenco.manager.model.available_job_setting import AvailableJobSetting\nfrom flamenco.manager.model.available_job_setting_eval_info import AvailableJobSettingEvalInfo\nfrom flamenco.manager.model.available_job_setting_subtype import AvailableJobSettingSubtype\nfrom flamenco.manager.model.available_job_setting_type import AvailableJobSettingType\nfrom flamenco.manager.model.available_job_setting_visibility import AvailableJobSettingVisibility\nfrom flamenco.manager.model.available_job_type import AvailableJobType\nfrom flamenco.manager.model.available_job_types import AvailableJobTypes\nfrom flamenco.manager.model.blender_path_check_result import BlenderPathCheckResult\nfrom flamenco.manager.model.blender_path_find_result import BlenderPathFindResult\nfrom flamenco.manager.model.blender_path_source import BlenderPathSource\nfrom flamenco.manager.model.command import Command\nfrom flamenco.manager.model.error import Error\nfrom flamenco.manager.model.event_farm_status import EventFarmStatus\nfrom flamenco.manager.model.event_job_update import EventJobUpdate\nfrom flamenco.manager.model.event_last_rendered_update import EventLastRenderedUpdate\nfrom flamenco.manager.model.event_life_cycle import EventLifeCycle\nfrom flamenco.manager.model.event_task_log_update import EventTaskLogUpdate\nfrom flamenco.manager.model.event_task_update import EventTaskUpdate\nfrom flamenco.manager.model.event_worker_tag_update import EventWorkerTagUpdate\nfrom flamenco.manager.model.event_worker_update import EventWorkerUpdate\nfrom flamenco.manager.model.farm_status import FarmStatus",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# flake8: noqa"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "# import all models into this package"
          },
          {
            "lineNumber": 4,
            "text": "# if you have many models here with many references from one model to another this may"
          },
          {
            "lineNumber": 5,
            "text": "# raise a RecursionError"
          },
          {
            "lineNumber": 6,
            "text": "# to avoid this, import only the models that you directly need like:"
          },
          {
            "lineNumber": 7,
            "text": "# from from flamenco.manager.model.pet import Pet"
          },
          {
            "lineNumber": 8,
            "text": "# or import this package, but before doing it, use:"
          },
          {
            "lineNumber": 9,
            "text": "# import sys"
          },
          {
            "lineNumber": 10,
            "text": "# sys.setrecursionlimit(n)"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "from flamenco.manager.model.assigned_task import AssignedTask"
          },
          {
            "lineNumber": 13,
            "text": "from flamenco.manager.model.assigned_worker import AssignedWorker"
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model.available_job_setting import AvailableJobSetting"
          },
          {
            "lineNumber": 15,
            "text": "from flamenco.manager.model.available_job_setting_eval_info import AvailableJobSettingEvalInfo"
          },
          {
            "lineNumber": 16,
            "text": "from flamenco.manager.model.available_job_setting_subtype import AvailableJobSettingSubtype"
          },
          {
            "lineNumber": 17,
            "text": "from flamenco.manager.model.available_job_setting_type import AvailableJobSettingType"
          },
          {
            "lineNumber": 18,
            "text": "from flamenco.manager.model.available_job_setting_visibility import AvailableJobSettingVisibility"
          },
          {
            "lineNumber": 19,
            "text": "from flamenco.manager.model.available_job_type import AvailableJobType"
          },
          {
            "lineNumber": 20,
            "text": "from flamenco.manager.model.available_job_types import AvailableJobTypes"
          },
          {
            "lineNumber": 21,
            "text": "from flamenco.manager.model.blender_path_check_result import BlenderPathCheckResult"
          },
          {
            "lineNumber": 22,
            "text": "from flamenco.manager.model.blender_path_find_result import BlenderPathFindResult"
          },
          {
            "lineNumber": 23,
            "text": "from flamenco.manager.model.blender_path_source import BlenderPathSource"
          },
          {
            "lineNumber": 24,
            "text": "from flamenco.manager.model.command import Command"
          },
          {
            "lineNumber": 25,
            "text": "from flamenco.manager.model.error import Error"
          },
          {
            "lineNumber": 26,
            "text": "from flamenco.manager.model.event_farm_status import EventFarmStatus"
          },
          {
            "lineNumber": 27,
            "text": "from flamenco.manager.model.event_job_update import EventJobUpdate"
          },
          {
            "lineNumber": 28,
            "text": "from flamenco.manager.model.event_last_rendered_update import EventLastRenderedUpdate"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.model.event_life_cycle import EventLifeCycle"
          },
          {
            "lineNumber": 30,
            "text": "from flamenco.manager.model.event_task_log_update import EventTaskLogUpdate"
          },
          {
            "lineNumber": 31,
            "text": "from flamenco.manager.model.event_task_update import EventTaskUpdate"
          },
          {
            "lineNumber": 32,
            "text": "from flamenco.manager.model.event_worker_tag_update import EventWorkerTagUpdate"
          },
          {
            "lineNumber": 33,
            "text": "from flamenco.manager.model.event_worker_update import EventWorkerUpdate"
          },
          {
            "lineNumber": 34,
            "text": "from flamenco.manager.model.farm_status import FarmStatus"
          }
        ],
        "range": {
          "endPosition": {
            "column": 57,
            "line": 33
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\models\\__init__.py",
        "signatures": {}
      },
      "score": 0.36295008659362793
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n# \u003cpep8 compliant\u003e\n\nimport datetime\nimport logging\nimport time\nfrom pathlib import Path, PurePosixPath\nfrom typing import Optional, TYPE_CHECKING\nfrom urllib3.exceptions import HTTPError, MaxRetryError\n\nimport bpy\n\nfrom . import job_types, job_submission, preferences, manager_info\nfrom .job_types_propgroup import JobTypePropertyGroup\nfrom .bat.submodules import bpathlib\n\nif TYPE_CHECKING:\n    from .bat.interface import (\n        PackThread as _PackThread,\n        Message as _Message,\n    )\n    from .manager.models import (\n        Error as _Error,\n        SubmittedJob as _SubmittedJob,\n    )\n    from .manager.api_client import ApiClient as _ApiClient\n    from .manager.exceptions import ApiException as _ApiException\nelse:\n    _PackThread = object\n    _Message = object\n    _SubmittedJob = object\n    _ApiClient = object\n    _ApiException = object\n    _Error = object\n\n_log = logging.getLogger(__name__)\n\n\nclass FlamencoOpMixin:\n    @staticmethod\n    def get_api_client(context):\n        \"\"\"Get a Flamenco API client to talk to the Manager.\n\n        Getting the client also loads the dependencies, so only import things\n        from &#96;flamenco.manager&#96; after calling this function.\n        \"\"\"\n        from . import comms, preferences\n\n        manager_url = preferences.manager_url(context)\n        api_client = comms.flamenco_api_client(manager_url)\n        return api_client\n\n\nclass FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):\n    bl_idname = \"flamenco.ping_manager\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "# \u003cpep8 compliant\u003e"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "import datetime"
          },
          {
            "lineNumber": 5,
            "text": "import logging"
          },
          {
            "lineNumber": 6,
            "text": "import time"
          },
          {
            "lineNumber": 7,
            "text": "from pathlib import Path, PurePosixPath"
          },
          {
            "lineNumber": 8,
            "text": "from typing import Optional, TYPE_CHECKING"
          },
          {
            "lineNumber": 9,
            "text": "from urllib3.exceptions import HTTPError, MaxRetryError"
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import bpy"
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "from . import job_types, job_submission, preferences, manager_info"
          },
          {
            "lineNumber": 14,
            "text": "from .job_types_propgroup import JobTypePropertyGroup"
          },
          {
            "lineNumber": 15,
            "text": "from .bat.submodules import bpathlib"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 18,
            "text": "    from .bat.interface import ("
          },
          {
            "lineNumber": 19,
            "text": "        PackThread as _PackThread,"
          },
          {
            "lineNumber": 20,
            "text": "        Message as _Message,"
          },
          {
            "lineNumber": 21,
            "text": "    )"
          },
          {
            "lineNumber": 22,
            "text": "    from .manager.models import ("
          },
          {
            "lineNumber": 23,
            "text": "        Error as _Error,"
          },
          {
            "lineNumber": 24,
            "text": "        SubmittedJob as _SubmittedJob,"
          },
          {
            "lineNumber": 25,
            "text": "    )"
          },
          {
            "lineNumber": 26,
            "text": "    from .manager.api_client import ApiClient as _ApiClient"
          },
          {
            "lineNumber": 27,
            "text": "    from .manager.exceptions import ApiException as _ApiException"
          },
          {
            "lineNumber": 28,
            "text": "else:"
          },
          {
            "lineNumber": 29,
            "text": "    _PackThread = object"
          },
          {
            "lineNumber": 30,
            "text": "    _Message = object"
          },
          {
            "lineNumber": 31,
            "text": "    _SubmittedJob = object"
          },
          {
            "lineNumber": 32,
            "text": "    _ApiClient = object"
          },
          {
            "lineNumber": 33,
            "text": "    _ApiException = object"
          },
          {
            "lineNumber": 34,
            "text": "    _Error = object"
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36,
            "text": "_log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38
          },
          {
            "isSignature": true,
            "lineNumber": 39,
            "text": "class FlamencoOpMixin:"
          },
          {
            "lineNumber": 40,
            "text": "    @staticmethod"
          },
          {
            "lineNumber": 41,
            "text": "    def get_api_client(context):"
          },
          {
            "lineNumber": 42,
            "text": "        \"\"\"Get a Flamenco API client to talk to the Manager."
          },
          {
            "lineNumber": 43
          },
          {
            "lineNumber": 44,
            "text": "        Getting the client also loads the dependencies, so only import things"
          },
          {
            "lineNumber": 45,
            "text": "        from &#96;flamenco.manager&#96; after calling this function."
          },
          {
            "lineNumber": 46,
            "text": "        \"\"\""
          },
          {
            "lineNumber": 47,
            "text": "        from . import comms, preferences"
          },
          {
            "lineNumber": 48
          },
          {
            "lineNumber": 49,
            "text": "        manager_url = preferences.manager_url(context)"
          },
          {
            "lineNumber": 50,
            "text": "        api_client = comms.flamenco_api_client(manager_url)"
          },
          {
            "isSignature": true,
            "lineNumber": 51,
            "text": "        return api_client"
          },
          {
            "lineNumber": 52
          },
          {
            "lineNumber": 53
          },
          {
            "isSignature": true,
            "lineNumber": 54,
            "text": "class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):"
          },
          {
            "lineNumber": 55,
            "text": "    bl_idname = \"flamenco.ping_manager\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 70
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\operators.py",
        "signatures": {}
      },
      "score": 0.36223849654197693
    },
    {
      "codeBlock": {
        "contents": "# flake8: noqa\n\n# Import all APIs into this package.\n# If you have many APIs here with many many models used in each API this may\n# raise a &#96;RecursionError&#96;.\n# In order to avoid this, import only the API that you directly need like:\n#\n#   from .api.jobs_api import JobsApi\n#\n# or import this package, but before doing it, use:\n#\n#   import sys\n#   sys.setrecursionlimit(n)\n\n# Import APIs into API package:\nfrom flamenco.manager.api.jobs_api import JobsApi\nfrom flamenco.manager.api.meta_api import MetaApi\nfrom flamenco.manager.api.shaman_api import ShamanApi\nfrom flamenco.manager.api.worker_api import WorkerApi\nfrom flamenco.manager.api.worker_mgt_api import WorkerMgtApi",
        "detailedLines": [
          {
            "lineNumber": 2,
            "text": "# flake8: noqa"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "# Import all APIs into this package."
          },
          {
            "lineNumber": 5,
            "text": "# If you have many APIs here with many many models used in each API this may"
          },
          {
            "lineNumber": 6,
            "text": "# raise a &#96;RecursionError&#96;."
          },
          {
            "lineNumber": 7,
            "text": "# In order to avoid this, import only the API that you directly need like:"
          },
          {
            "lineNumber": 8,
            "text": "#"
          },
          {
            "lineNumber": 9,
            "text": "#   from .api.jobs_api import JobsApi"
          },
          {
            "lineNumber": 10,
            "text": "#"
          },
          {
            "lineNumber": 11,
            "text": "# or import this package, but before doing it, use:"
          },
          {
            "lineNumber": 12,
            "text": "#"
          },
          {
            "lineNumber": 13,
            "text": "#   import sys"
          },
          {
            "lineNumber": 14,
            "text": "#   sys.setrecursionlimit(n)"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "# Import APIs into API package:"
          },
          {
            "lineNumber": 17,
            "text": "from flamenco.manager.api.jobs_api import JobsApi"
          },
          {
            "lineNumber": 18,
            "text": "from flamenco.manager.api.meta_api import MetaApi"
          },
          {
            "lineNumber": 19,
            "text": "from flamenco.manager.api.shaman_api import ShamanApi"
          },
          {
            "lineNumber": 20,
            "text": "from flamenco.manager.api.worker_api import WorkerApi"
          },
          {
            "lineNumber": 21,
            "text": "from flamenco.manager.api.worker_mgt_api import WorkerMgtApi"
          }
        ],
        "range": {
          "endPosition": {
            "column": 60,
            "line": 20
          },
          "startPosition": {
            "line": 1
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\apis\\__init__.py",
        "signatures": {}
      },
      "score": 0.36044394969940186
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n\n\"\"\"External dependencies loader.\"\"\"\n\nimport contextlib\nimport importlib\nfrom pathlib import Path\nimport sys\nimport logging\nfrom types import ModuleType\nfrom typing import Iterator, Iterable\n\n_my_dir = Path(__file__).parent\n_log = logging.getLogger(__name__)\n\n\ndef load_wheel(module_name: str, submodules: Iterable[str]) -\u003e list[ModuleType]:\n    \"\"\"Loads modules from a wheel file 'module_name*.whl'.\n\n    Loads &#96;module_name&#96;, and if submodules are given, loads\n    &#96;module_name.submodule&#96; for each of the submodules. This allows loading all\n    required modules from the same wheel in one session, ensuring that\n    inter-submodule references are correct.\n\n    Returns the loaded modules, so [module, submodule, submodule, ...].\n    \"\"\"\n\n    fname_prefix = _fname_prefix_from_module_name(module_name)\n    wheel = _wheel_filename(fname_prefix)\n\n    loaded_modules: list[ModuleType] = []\n    to_load = [module_name] + [f\"{module_name}.{submodule}\" for submodule in submodules]\n\n    # Load the module from the wheel file. Keep a backup of sys.path so that it\n    # can be restored later. This should ensure that future import statements\n    # cannot find this wheel file, increasing the separation of dependencies of\n    # this add-on from other add-ons.\n    with _sys_path_mod_backup(wheel):\n        for modname in to_load:\n            try:\n                module = importlib.import_module(modname)\n            except ImportError as ex:\n                raise ImportError(\n                    \"Unable to load %r from %s: %s\" % (modname, wheel, ex)\n                ) from None\n            assert isinstance(module, ModuleType)\n            loaded_modules.append(module)\n            _log.info(\"Loaded %s from %s\", modname, module.__file__)\n\n    assert len(loaded_modules) == len(\n        to_load\n    ), f\"expecting to load {len(to_load)} modules, but only have {len(loaded_modules)}: {loaded_modules}\"\n    return loaded_modules\n\n\ndef load_wheel_global(module_name: str, fname_prefix: str = \"\") -\u003e ModuleType:\n    ",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "\"\"\"External dependencies loader.\"\"\""
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "import contextlib"
          },
          {
            "lineNumber": 6,
            "text": "import importlib"
          },
          {
            "lineNumber": 7,
            "text": "from pathlib import Path"
          },
          {
            "lineNumber": 8,
            "text": "import sys"
          },
          {
            "lineNumber": 9,
            "text": "import logging"
          },
          {
            "lineNumber": 10,
            "text": "from types import ModuleType"
          },
          {
            "lineNumber": 11,
            "text": "from typing import Iterator, Iterable"
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "_my_dir = Path(__file__).parent"
          },
          {
            "lineNumber": 14,
            "text": "_log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16
          },
          {
            "isSignature": true,
            "lineNumber": 17,
            "text": "def load_wheel(module_name: str, submodules: Iterable[str]) -\u003e list[ModuleType]:"
          },
          {
            "lineNumber": 18,
            "text": "    \"\"\"Loads modules from a wheel file 'module_name*.whl'."
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20,
            "text": "    Loads &#96;module_name&#96;, and if submodules are given, loads"
          },
          {
            "lineNumber": 21,
            "text": "    &#96;module_name.submodule&#96; for each of the submodules. This allows loading all"
          },
          {
            "lineNumber": 22,
            "text": "    required modules from the same wheel in one session, ensuring that"
          },
          {
            "lineNumber": 23,
            "text": "    inter-submodule references are correct."
          },
          {
            "lineNumber": 24
          },
          {
            "lineNumber": 25,
            "text": "    Returns the loaded modules, so [module, submodule, submodule, ...]."
          },
          {
            "lineNumber": 26,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 27
          },
          {
            "lineNumber": 28,
            "text": "    fname_prefix = _fname_prefix_from_module_name(module_name)"
          },
          {
            "lineNumber": 29,
            "text": "    wheel = _wheel_filename(fname_prefix)"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "    loaded_modules: list[ModuleType] = []"
          },
          {
            "lineNumber": 32,
            "text": "    to_load = [module_name] + [f\"{module_name}.{submodule}\" for submodule in submodules]"
          },
          {
            "lineNumber": 33
          },
          {
            "lineNumber": 34,
            "text": "    # Load the module from the wheel file. Keep a backup of sys.path so that it"
          },
          {
            "lineNumber": 35,
            "text": "    # can be restored later. This should ensure that future import statements"
          },
          {
            "lineNumber": 36,
            "text": "    # cannot find this wheel file, increasing the separation of dependencies of"
          },
          {
            "lineNumber": 37,
            "text": "    # this add-on from other add-ons."
          },
          {
            "lineNumber": 38,
            "text": "    with _sys_path_mod_backup(wheel):"
          },
          {
            "lineNumber": 39,
            "text": "        for modname in to_load:"
          },
          {
            "lineNumber": 40,
            "text": "            try:"
          },
          {
            "lineNumber": 41,
            "text": "                module = importlib.import_module(modname)"
          },
          {
            "lineNumber": 42,
            "text": "            except ImportError as ex:"
          },
          {
            "lineNumber": 43,
            "text": "                raise ImportError("
          },
          {
            "lineNumber": 44,
            "text": "                    \"Unable to load %r from %s: %s\" % (modname, wheel, ex)"
          },
          {
            "lineNumber": 45,
            "text": "                ) from None"
          },
          {
            "lineNumber": 46,
            "text": "            assert isinstance(module, ModuleType)"
          },
          {
            "lineNumber": 47,
            "text": "            loaded_modules.append(module)"
          },
          {
            "lineNumber": 48,
            "text": "            _log.info(\"Loaded %s from %s\", modname, module.__file__)"
          },
          {
            "lineNumber": 49
          },
          {
            "lineNumber": 50,
            "text": "    assert len(loaded_modules) == len("
          },
          {
            "lineNumber": 51,
            "text": "        to_load"
          },
          {
            "lineNumber": 52,
            "text": "    ), f\"expecting to load {len(to_load)} modules, but only have {len(loaded_modules)}: {loaded_modules}\""
          },
          {
            "isSignature": true,
            "lineNumber": 53,
            "text": "    return loaded_modules"
          },
          {
            "lineNumber": 54
          },
          {
            "lineNumber": 55
          },
          {
            "isSignature": true,
            "lineNumber": 56,
            "text": "def load_wheel_global(module_name: str, fname_prefix: str = \"\") -\u003e ModuleType:"
          },
          {
            "lineNumber": 57,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 95
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\wheels\\__init__.py",
        "signatures": {}
      },
      "score": 0.3576132655143738
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n# \u003cpep8 compliant\u003e\n\nfrom pathlib import Path\nfrom typing import Callable\nimport dataclasses\n\nfrom .bat.submodules import bpathlib\n\n\ndef for_blendfile(blendfile: Path, strategy: str) -\u003e Path:\n    \"\"\"Return what is considered to be the project directory containing the given file.\n\n    If none can be found, the directory containing the current blend file is returned.\n    If the current blend file has no path (because it was not saved), a ValueError is raised.\n\n    :param blendfile: the path of the blend file for which to find the project.\n    :param strategy: the name of the finder to use, see &#96;finders&#96;.\n    \"\"\"\n    if blendfile.is_dir():\n        msg = f\"{blendfile} is not a blend file, cannot find project directory\"\n        raise ValueError(msg)\n\n    try:\n        finder_info = finders[strategy]\n    except KeyError:\n        msg = f\"Unknown strategy {strategy!r}, cannot find project directory\"\n        raise ValueError(msg) from None\n\n    return finder_info.finder(blendfile)\n\n\ndef _finder_blender_project(blendfile: Path) -\u003e Path:\n    return _search_path_marker(blendfile, \".blender_project\")",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "# \u003cpep8 compliant\u003e"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "from pathlib import Path"
          },
          {
            "lineNumber": 5,
            "text": "from typing import Callable"
          },
          {
            "lineNumber": 6,
            "text": "import dataclasses"
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "from .bat.submodules import bpathlib"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "isSignature": true,
            "lineNumber": 11,
            "text": "def for_blendfile(blendfile: Path, strategy: str) -\u003e Path:"
          },
          {
            "lineNumber": 12,
            "text": "    \"\"\"Return what is considered to be the project directory containing the given file."
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "    If none can be found, the directory containing the current blend file is returned."
          },
          {
            "lineNumber": 15,
            "text": "    If the current blend file has no path (because it was not saved), a ValueError is raised."
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "    :param blendfile: the path of the blend file for which to find the project."
          },
          {
            "lineNumber": 18,
            "text": "    :param strategy: the name of the finder to use, see &#96;finders&#96;."
          },
          {
            "lineNumber": 19,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 20,
            "text": "    if blendfile.is_dir():"
          },
          {
            "lineNumber": 21,
            "text": "        msg = f\"{blendfile} is not a blend file, cannot find project directory\""
          },
          {
            "lineNumber": 22,
            "text": "        raise ValueError(msg)"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "    try:"
          },
          {
            "lineNumber": 25,
            "text": "        finder_info = finders[strategy]"
          },
          {
            "lineNumber": 26,
            "text": "    except KeyError:"
          },
          {
            "lineNumber": 27,
            "text": "        msg = f\"Unknown strategy {strategy!r}, cannot find project directory\""
          },
          {
            "lineNumber": 28,
            "text": "        raise ValueError(msg) from None"
          },
          {
            "lineNumber": 29
          },
          {
            "isSignature": true,
            "lineNumber": 30,
            "text": "    return finder_info.finder(blendfile)"
          },
          {
            "lineNumber": 31
          },
          {
            "lineNumber": 32
          },
          {
            "isSignature": true,
            "lineNumber": 33,
            "text": "def _finder_blender_project(blendfile: Path) -\u003e Path:"
          },
          {
            "isSignature": true,
            "lineNumber": 34,
            "text": "    return _search_path_marker(blendfile, \".blender_project\")"
          }
        ],
        "range": {
          "endPosition": {
            "column": 61,
            "line": 33
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\projects.py",
        "signatures": {}
      },
      "score": 0.35720279812812805
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n\n# \u003cpep8 compliant\u003e\n\nimport logging\nfrom typing import TYPE_CHECKING\n\nimport bpy\n\nfrom flamenco import manager_info, job_types\n\n_flamenco_client = None\n_log = logging.getLogger(__name__)\n\nif TYPE_CHECKING:\n    from flamenco.manager import ApiClient as _ApiClient\n    from flamenco.manager.models import (\n        FlamencoVersion as _FlamencoVersion,\n        SharedStorageLocation as _SharedStorageLocation,\n    )\n    from .preferences import FlamencoPreferences as _FlamencoPreferences\nelse:\n    _ApiClient = object\n    _FlamencoPreferences = object\n    _FlamencoVersion = object\n    _SharedStorageLocation = object\n\n\ndef flamenco_api_client(manager_url: str) -\u003e _ApiClient:\n    \"\"\"Returns an API client for communicating with a Manager.\"\"\"\n    global _flamenco_client\n\n    if _flamenco_client is not None:\n        return _flamenco_client\n\n    from . import dependencies\n\n    dependencies.preload_modules()\n\n    from . import manager\n\n    configuration = manager.Configuration(host=manager_url.rstrip(\"/\"))\n    _flamenco_client = manager.ApiClient(configuration)\n    _log.info(\"created API client for Manager at %s\", manager_url)\n\n    return _flamenco_client\n\n\ndef flamenco_client_version() -\u003e str:\n    \"\"\"Return the version of the Flamenco OpenAPI client.\"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "# \u003cpep8 compliant\u003e"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "import logging"
          },
          {
            "lineNumber": 6,
            "text": "from typing import TYPE_CHECKING"
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "import bpy"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "from flamenco import manager_info, job_types"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "_flamenco_client = None"
          },
          {
            "lineNumber": 13,
            "text": "_log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 14
          },
          {
            "lineNumber": 15,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 16,
            "text": "    from flamenco.manager import ApiClient as _ApiClient"
          },
          {
            "lineNumber": 17,
            "text": "    from flamenco.manager.models import ("
          },
          {
            "lineNumber": 18,
            "text": "        FlamencoVersion as _FlamencoVersion,"
          },
          {
            "lineNumber": 19,
            "text": "        SharedStorageLocation as _SharedStorageLocation,"
          },
          {
            "lineNumber": 20,
            "text": "    )"
          },
          {
            "lineNumber": 21,
            "text": "    from .preferences import FlamencoPreferences as _FlamencoPreferences"
          },
          {
            "lineNumber": 22,
            "text": "else:"
          },
          {
            "lineNumber": 23,
            "text": "    _ApiClient = object"
          },
          {
            "lineNumber": 24,
            "text": "    _FlamencoPreferences = object"
          },
          {
            "lineNumber": 25,
            "text": "    _FlamencoVersion = object"
          },
          {
            "lineNumber": 26,
            "text": "    _SharedStorageLocation = object"
          },
          {
            "lineNumber": 27
          },
          {
            "lineNumber": 28
          },
          {
            "isSignature": true,
            "lineNumber": 29,
            "text": "def flamenco_api_client(manager_url: str) -\u003e _ApiClient:"
          },
          {
            "lineNumber": 30,
            "text": "    \"\"\"Returns an API client for communicating with a Manager.\"\"\""
          },
          {
            "lineNumber": 31,
            "text": "    global _flamenco_client"
          },
          {
            "lineNumber": 32
          },
          {
            "lineNumber": 33,
            "text": "    if _flamenco_client is not None:"
          },
          {
            "lineNumber": 34,
            "text": "        return _flamenco_client"
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36,
            "text": "    from . import dependencies"
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38,
            "text": "    dependencies.preload_modules()"
          },
          {
            "lineNumber": 39
          },
          {
            "lineNumber": 40,
            "text": "    from . import manager"
          },
          {
            "lineNumber": 41
          },
          {
            "lineNumber": 42,
            "text": "    configuration = manager.Configuration(host=manager_url.rstrip(\"/\"))"
          },
          {
            "lineNumber": 43,
            "text": "    _flamenco_client = manager.ApiClient(configuration)"
          },
          {
            "lineNumber": 44,
            "text": "    _log.info(\"created API client for Manager at %s\", manager_url)"
          },
          {
            "lineNumber": 45
          },
          {
            "isSignature": true,
            "lineNumber": 46,
            "text": "    return _flamenco_client"
          },
          {
            "lineNumber": 47
          },
          {
            "lineNumber": 48
          },
          {
            "isSignature": true,
            "lineNumber": 49,
            "text": "def flamenco_client_version() -\u003e str:"
          },
          {
            "lineNumber": 50,
            "text": "    \"\"\"Return the version of the Flamenco OpenAPI client.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 57
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\comms.py",
        "signatures": {}
      },
      "score": 0.3551279902458191
    },
    {
      "codeBlock": {
        "contents": "# flake8: noqa\n\n\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\n__version__ = \"3.8.2\"\n\n# import ApiClient\nfrom flamenco.manager.api_client import ApiClient\n\n# import Configuration\nfrom flamenco.manager.configuration import Configuration\n\n# import exceptions\nfrom flamenco.manager.exceptions import OpenApiException\nfrom flamenco.manager.exceptions import ApiAttributeError\nfrom flamenco.manager.exceptions import ApiTypeError\nfrom flamenco.manager.exceptions import ApiValueError\nfrom flamenco.manager.exceptions import ApiKeyError\nfrom flamenco.manager.exceptions import ApiException",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# flake8: noqa"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "\"\"\""
          },
          {
            "lineNumber": 4,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 9,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 10,
            "text": "\"\"\""
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "__version__ = \"3.8.2\""
          },
          {
            "lineNumber": 14
          },
          {
            "lineNumber": 15,
            "text": "# import ApiClient"
          },
          {
            "lineNumber": 16,
            "text": "from flamenco.manager.api_client import ApiClient"
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "# import Configuration"
          },
          {
            "lineNumber": 19,
            "text": "from flamenco.manager.configuration import Configuration"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21,
            "text": "# import exceptions"
          },
          {
            "lineNumber": 22,
            "text": "from flamenco.manager.exceptions import OpenApiException"
          },
          {
            "lineNumber": 23,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 24,
            "text": "from flamenco.manager.exceptions import ApiTypeError"
          },
          {
            "lineNumber": 25,
            "text": "from flamenco.manager.exceptions import ApiValueError"
          },
          {
            "lineNumber": 26,
            "text": "from flamenco.manager.exceptions import ApiKeyError"
          },
          {
            "lineNumber": 27,
            "text": "from flamenco.manager.exceptions import ApiException"
          }
        ],
        "range": {
          "endPosition": {
            "column": 52,
            "line": 26
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\__init__.py",
        "signatures": {}
      },
      "score": 0.3548923134803772
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n# \u003cpep8 compliant\u003e\n\nimport dataclasses\nimport json\nimport platform\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Optional, Union\n\nfrom urllib3.exceptions import HTTPError, MaxRetryError\n\nimport bpy\n\nif TYPE_CHECKING:\n    from flamenco.manager import ApiClient as _ApiClient\n    from flamenco.manager.models import (\n        AvailableJobTypes as _AvailableJobTypes,\n        FlamencoVersion as _FlamencoVersion,\n        SharedStorageLocation as _SharedStorageLocation,\n        WorkerTagList as _WorkerTagList,\n    )\nelse:\n    _ApiClient = object\n    _AvailableJobTypes = object\n    _FlamencoVersion = object\n    _SharedStorageLocation = object\n    _WorkerTagList = object\n\n\n@dataclasses.dataclass\nclass ManagerInfo:\n    \"\"\"Cached information obtained from a Flamenco Manager.\n\n    This is the root object of what is stored on disk, every time someone\n    presses a 'refresh' button to update worker tags, job types, etc.\n    \"\"\"\n\n    flamenco_version: _FlamencoVersion\n    shared_storage: _SharedStorageLocation\n    job_types: _AvailableJobTypes\n    worker_tags: _WorkerTagList\n\n    @staticmethod\n    def type_info() -\u003e dict[str, type]:\n        # Do a late import, so that the API is only imported when actually used.\n        from flamenco.manager.models import (\n            AvailableJobTypes,\n            FlamencoVersion,\n            SharedStorageLocation,\n            WorkerTagList,\n        )\n\n        # These types cannot be obtained by introspecting the ManagerInfo class, as\n        # at runtime that doesn't use real type annotations.\n        return {\n            \"flamenco_version\": FlamencoVersion,\n            \"shared_storage\": SharedStorageLocation,\n            \"job_types\": AvailableJobTypes,\n            \"worker_tags\": WorkerTagList,\n        }\n\n\nclass FetchError(RuntimeError):\n    \"\"\"Raised when the manager info could not be fetched from the Manager.\"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "# \u003cpep8 compliant\u003e"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "import dataclasses"
          },
          {
            "lineNumber": 5,
            "text": "import json"
          },
          {
            "lineNumber": 6,
            "text": "import platform"
          },
          {
            "lineNumber": 7,
            "text": "from pathlib import Path"
          },
          {
            "lineNumber": 8,
            "text": "from typing import TYPE_CHECKING, Optional, Union"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "from urllib3.exceptions import HTTPError, MaxRetryError"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "import bpy"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 15,
            "text": "    from flamenco.manager import ApiClient as _ApiClient"
          },
          {
            "lineNumber": 16,
            "text": "    from flamenco.manager.models import ("
          },
          {
            "lineNumber": 17,
            "text": "        AvailableJobTypes as _AvailableJobTypes,"
          },
          {
            "lineNumber": 18,
            "text": "        FlamencoVersion as _FlamencoVersion,"
          },
          {
            "lineNumber": 19,
            "text": "        SharedStorageLocation as _SharedStorageLocation,"
          },
          {
            "lineNumber": 20,
            "text": "        WorkerTagList as _WorkerTagList,"
          },
          {
            "lineNumber": 21,
            "text": "    )"
          },
          {
            "lineNumber": 22,
            "text": "else:"
          },
          {
            "lineNumber": 23,
            "text": "    _ApiClient = object"
          },
          {
            "lineNumber": 24,
            "text": "    _AvailableJobTypes = object"
          },
          {
            "lineNumber": 25,
            "text": "    _FlamencoVersion = object"
          },
          {
            "lineNumber": 26,
            "text": "    _SharedStorageLocation = object"
          },
          {
            "lineNumber": 27,
            "text": "    _WorkerTagList = object"
          },
          {
            "lineNumber": 28
          },
          {
            "lineNumber": 29
          },
          {
            "lineNumber": 30,
            "text": "@dataclasses.dataclass"
          },
          {
            "isSignature": true,
            "lineNumber": 31,
            "text": "class ManagerInfo:"
          },
          {
            "lineNumber": 32,
            "text": "    \"\"\"Cached information obtained from a Flamenco Manager."
          },
          {
            "lineNumber": 33
          },
          {
            "lineNumber": 34,
            "text": "    This is the root object of what is stored on disk, every time someone"
          },
          {
            "lineNumber": 35,
            "text": "    presses a 'refresh' button to update worker tags, job types, etc."
          },
          {
            "lineNumber": 36,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38,
            "text": "    flamenco_version: _FlamencoVersion"
          },
          {
            "lineNumber": 39,
            "text": "    shared_storage: _SharedStorageLocation"
          },
          {
            "lineNumber": 40,
            "text": "    job_types: _AvailableJobTypes"
          },
          {
            "lineNumber": 41,
            "text": "    worker_tags: _WorkerTagList"
          },
          {
            "lineNumber": 42
          },
          {
            "lineNumber": 43,
            "text": "    @staticmethod"
          },
          {
            "lineNumber": 44,
            "text": "    def type_info() -\u003e dict[str, type]:"
          },
          {
            "lineNumber": 45,
            "text": "        # Do a late import, so that the API is only imported when actually used."
          },
          {
            "lineNumber": 46,
            "text": "        from flamenco.manager.models import ("
          },
          {
            "lineNumber": 47,
            "text": "            AvailableJobTypes,"
          },
          {
            "lineNumber": 48,
            "text": "            FlamencoVersion,"
          },
          {
            "lineNumber": 49,
            "text": "            SharedStorageLocation,"
          },
          {
            "lineNumber": 50,
            "text": "            WorkerTagList,"
          },
          {
            "lineNumber": 51,
            "text": "        )"
          },
          {
            "lineNumber": 52
          },
          {
            "lineNumber": 53,
            "text": "        # These types cannot be obtained by introspecting the ManagerInfo class, as"
          },
          {
            "lineNumber": 54,
            "text": "        # at runtime that doesn't use real type annotations."
          },
          {
            "lineNumber": 55,
            "text": "        return {"
          },
          {
            "lineNumber": 56,
            "text": "            \"flamenco_version\": FlamencoVersion,"
          },
          {
            "lineNumber": 57,
            "text": "            \"shared_storage\": SharedStorageLocation,"
          },
          {
            "lineNumber": 58,
            "text": "            \"job_types\": AvailableJobTypes,"
          },
          {
            "lineNumber": 59,
            "text": "            \"worker_tags\": WorkerTagList,"
          },
          {
            "isSignature": true,
            "lineNumber": 60,
            "text": "        }"
          },
          {
            "lineNumber": 61
          },
          {
            "lineNumber": 62
          },
          {
            "isSignature": true,
            "lineNumber": 63,
            "text": "class FetchError(RuntimeError):"
          },
          {
            "isSignature": true,
            "lineNumber": 64,
            "text": "    \"\"\"Raised when the manager info could not be fetched from the Manager.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "column": 77,
            "line": 63
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager_info.py",
        "signatures": {}
      },
      "score": 0.35453975200653076
    },
    {
      "codeBlock": {
        "contents": "# OpenAPI Generator Ignore\n# Generated by openapi-generator https://github.com/openapitools/openapi-generator\n\n# Use this file to prevent files from being overwritten by the generator.\n# The patterns follow closely to .gitignore or .dockerignore.\n\n# As an example, the C# client generator defines ApiClient.cs.\n# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:\n#ApiClient.cs\n\n# You can match any string of characters against a directory, file or extension with a single asterisk (*):\n#foo/*/qux\n# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux\n\n# You can recursively match patterns against a directory, file or extension with a double asterisk (**):\n#foo/**/qux\n# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux\n\n# You can also negate patterns with an exclamation (!).\n# For example, you can ignore all files in a docs folder with the file extension .md:\n#docs/*.md\n# Then explicitly reverse the ignore rule for a single file:\n#!docs/README.md\n\n# This file is written by a human, and should not be overwritten by the generator.\nflamenco/__init__.py",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# OpenAPI Generator Ignore"
          },
          {
            "lineNumber": 2,
            "text": "# Generated by openapi-generator https://github.com/openapitools/openapi-generator"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "# Use this file to prevent files from being overwritten by the generator."
          },
          {
            "lineNumber": 5,
            "text": "# The patterns follow closely to .gitignore or .dockerignore."
          },
          {
            "lineNumber": 6
          },
          {
            "lineNumber": 7,
            "text": "# As an example, the C# client generator defines ApiClient.cs."
          },
          {
            "lineNumber": 8,
            "text": "# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:"
          },
          {
            "lineNumber": 9,
            "text": "#ApiClient.cs"
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "# You can match any string of characters against a directory, file or extension with a single asterisk (*):"
          },
          {
            "lineNumber": 12,
            "text": "#foo/*/qux"
          },
          {
            "lineNumber": 13,
            "text": "# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux"
          },
          {
            "lineNumber": 14
          },
          {
            "lineNumber": 15,
            "text": "# You can recursively match patterns against a directory, file or extension with a double asterisk (**):"
          },
          {
            "lineNumber": 16,
            "text": "#foo/**/qux"
          },
          {
            "lineNumber": 17,
            "text": "# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux"
          },
          {
            "lineNumber": 18
          },
          {
            "lineNumber": 19,
            "text": "# You can also negate patterns with an exclamation (!)."
          },
          {
            "lineNumber": 20,
            "text": "# For example, you can ignore all files in a docs folder with the file extension .md:"
          },
          {
            "lineNumber": 21,
            "text": "#docs/*.md"
          },
          {
            "lineNumber": 22,
            "text": "# Then explicitly reverse the ignore rule for a single file:"
          },
          {
            "lineNumber": 23,
            "text": "#!docs/README.md"
          },
          {
            "lineNumber": 24
          },
          {
            "lineNumber": 25,
            "text": "# This file is written by a human, and should not be overwritten by the generator."
          },
          {
            "lineNumber": 26,
            "text": "flamenco/__init__.py"
          }
        ],
        "range": {
          "endPosition": {
            "line": 26
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\.openapi-generator-ignore",
        "signatures": {}
      },
      "score": 0.34923255443573
    },
    {
      "codeBlock": {
        "contents": "class FlamencoPreferences(bpy.types.AddonPreferences):\n    bl_idname = \"flamenco\"\n\n    manager_url: bpy.props.StringProperty(  # type: ignore\n        name=\"Manager URL\",\n        description=\"Location of the Manager\",\n        default=\"http://localhost:8080/\",\n        update=_manager_url_updated,\n    )\n\n    project_finder: bpy.props.EnumProperty(  # type: ignore\n        name=\"Project Finder\",\n        description=\"Strategy for Flamenco to find the top level directory of your project\",\n        default=_project_finder_enum_items[0][0],\n        items=_project_finder_enum_items,\n    )\n\n    # Property that gets its value from the above _job_storage, and cannot be\n    # set. This makes it read-only in the GUI.\n    job_storage_for_gui: bpy.props.StringProperty(  # type: ignore\n        name=\"Job Storage Directory\",\n        subtype=\"DIR_PATH\",\n        default=\"\",\n        options={\"SKIP_SAVE\"},\n        description=\"Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager\",\n        get=lambda prefs: prefs._job_storage(),\n    )\n\n    def draw(self, context: bpy.types.Context) -\u003e None:\n        layout = self.layout",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 51,
            "text": "class FlamencoPreferences(bpy.types.AddonPreferences):"
          },
          {
            "lineNumber": 52,
            "text": "    bl_idname = \"flamenco\""
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54,
            "text": "    manager_url: bpy.props.StringProperty(  # type: ignore"
          },
          {
            "lineNumber": 55,
            "text": "        name=\"Manager URL\","
          },
          {
            "lineNumber": 56,
            "text": "        description=\"Location of the Manager\","
          },
          {
            "lineNumber": 57,
            "text": "        default=\"http://localhost:8080/\","
          },
          {
            "lineNumber": 58,
            "text": "        update=_manager_url_updated,"
          },
          {
            "lineNumber": 59,
            "text": "    )"
          },
          {
            "lineNumber": 60
          },
          {
            "lineNumber": 61,
            "text": "    project_finder: bpy.props.EnumProperty(  # type: ignore"
          },
          {
            "lineNumber": 62,
            "text": "        name=\"Project Finder\","
          },
          {
            "lineNumber": 63,
            "text": "        description=\"Strategy for Flamenco to find the top level directory of your project\","
          },
          {
            "lineNumber": 64,
            "text": "        default=_project_finder_enum_items[0][0],"
          },
          {
            "lineNumber": 65,
            "text": "        items=_project_finder_enum_items,"
          },
          {
            "lineNumber": 66,
            "text": "    )"
          },
          {
            "lineNumber": 67
          },
          {
            "lineNumber": 68,
            "text": "    # Property that gets its value from the above _job_storage, and cannot be"
          },
          {
            "lineNumber": 69,
            "text": "    # set. This makes it read-only in the GUI."
          },
          {
            "lineNumber": 70,
            "text": "    job_storage_for_gui: bpy.props.StringProperty(  # type: ignore"
          },
          {
            "lineNumber": 71,
            "text": "        name=\"Job Storage Directory\","
          },
          {
            "lineNumber": 72,
            "text": "        subtype=\"DIR_PATH\","
          },
          {
            "lineNumber": 73,
            "text": "        default=\"\","
          },
          {
            "lineNumber": 74,
            "text": "        options={\"SKIP_SAVE\"},"
          },
          {
            "lineNumber": 75,
            "text": "        description=\"Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager\","
          },
          {
            "lineNumber": 76,
            "text": "        get=lambda prefs: prefs._job_storage(),"
          },
          {
            "lineNumber": 77,
            "text": "    )"
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "    def draw(self, context: bpy.types.Context) -\u003e None:"
          },
          {
            "lineNumber": 80,
            "text": "        layout = self.layout"
          }
        ],
        "range": {
          "endPosition": {
            "line": 136
          },
          "startPosition": {
            "column": 77,
            "line": 47
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\preferences.py",
        "signatures": {}
      },
      "score": 0.34826916456222534
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\nfrom pathlib import Path, PurePosixPath\nfrom typing import TYPE_CHECKING, Optional, Union\nimport platform\nimport logging\n\nimport bpy\n\nfrom .job_types_propgroup import JobTypePropertyGroup\nfrom .bat.submodules import bpathlib\nfrom . import manager_info\n\nif TYPE_CHECKING:\n    from .manager import ApiClient as _ApiClient\n    from .manager.models import (\n        AvailableJobType as _AvailableJobType,\n        Job as _Job,\n        SubmittedJob as _SubmittedJob,\n    )\nelse:\n    _AvailableJobType = object\n    _ApiClient = object\n    _Job = object\n    _SubmittedJob = object\n\n\n# If a job has a setting with this key, type 'str', and subtype 'file_path',\n# it'll be set to the path of the BAT-packed blend file.\nBLENDFILE_SETTING_KEY = \"blendfile\"\n\nlog = logging.getLogger(__name__)\n\n\ndef job_for_scene(scene: bpy.types.Scene) -\u003e Optional[_SubmittedJob]:\n    ",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "from pathlib import Path, PurePosixPath"
          },
          {
            "lineNumber": 3,
            "text": "from typing import TYPE_CHECKING, Optional, Union"
          },
          {
            "lineNumber": 4,
            "text": "import platform"
          },
          {
            "lineNumber": 5,
            "text": "import logging"
          },
          {
            "lineNumber": 6
          },
          {
            "lineNumber": 7,
            "text": "import bpy"
          },
          {
            "lineNumber": 8
          },
          {
            "lineNumber": 9,
            "text": "from .job_types_propgroup import JobTypePropertyGroup"
          },
          {
            "lineNumber": 10,
            "text": "from .bat.submodules import bpathlib"
          },
          {
            "lineNumber": 11,
            "text": "from . import manager_info"
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 14,
            "text": "    from .manager import ApiClient as _ApiClient"
          },
          {
            "lineNumber": 15,
            "text": "    from .manager.models import ("
          },
          {
            "lineNumber": 16,
            "text": "        AvailableJobType as _AvailableJobType,"
          },
          {
            "lineNumber": 17,
            "text": "        Job as _Job,"
          },
          {
            "lineNumber": 18,
            "text": "        SubmittedJob as _SubmittedJob,"
          },
          {
            "lineNumber": 19,
            "text": "    )"
          },
          {
            "lineNumber": 20,
            "text": "else:"
          },
          {
            "lineNumber": 21,
            "text": "    _AvailableJobType = object"
          },
          {
            "lineNumber": 22,
            "text": "    _ApiClient = object"
          },
          {
            "lineNumber": 23,
            "text": "    _Job = object"
          },
          {
            "lineNumber": 24,
            "text": "    _SubmittedJob = object"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26
          },
          {
            "lineNumber": 27,
            "text": "# If a job has a setting with this key, type 'str', and subtype 'file_path',"
          },
          {
            "lineNumber": 28,
            "text": "# it'll be set to the path of the BAT-packed blend file."
          },
          {
            "lineNumber": 29,
            "text": "BLENDFILE_SETTING_KEY = \"blendfile\""
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 32
          },
          {
            "lineNumber": 33
          },
          {
            "isSignature": true,
            "lineNumber": 34,
            "text": "def job_for_scene(scene: bpy.types.Scene) -\u003e Optional[_SubmittedJob]:"
          },
          {
            "lineNumber": 35,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 68
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\job_submission.py",
        "signatures": {}
      },
      "score": 0.3453158140182495
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    ApiTypeError,\n    ModelComposed,\n    ModelNormal,\n    ModelSimple,\n    cached_property,\n    change_keys_js_to_python,\n    convert_js_args_to_python_args,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_get_composed_info,\n    OpenApiModel\n)\nfrom flamenco.manager.exceptions import ApiAttributeError\n\n\ndef lazy_import():\n    from flamenco.manager.model.command import Command\n    from flamenco.manager.model.task_status import TaskStatus\n    from flamenco.manager.model.task_worker import TaskWorker\n    globals()['Command'] = Command\n    globals()['TaskStatus'] = TaskStatus\n    globals()['TaskWorker'] = TaskWorker\n\n\nclass Task(ModelNormal):\n    \"\"\"NOTE: This class is auto generated by OpenAPI Generator.\n    Ref: https://openapi-generator.tech\n\n    Do not edit the class manually.\n\n    Attributes:\n      allowed_values (dict): The key is the tuple path to the attribute\n          and the for var_name this is (var_name,). The value is a dict\n          with a capitalized key describing the allowed value and an allowed\n          value. These dicts store the allowed enum values.\n      attribute_map (dict): The key is attribute name\n          and the value is json key in definition.\n      discriminator_value_class_map (dict): A dict to go from the discriminator\n          variable value to the discriminator class name.\n      validations (dict): The key is the tuple path to the attribute\n          and the for var_name this is (var_name,). The value is a dict\n          that stores validations for max_length, min_length, max_items,\n          min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,\n          inclusive_minimum, and regex.\n      additional_properties_type (tuple): A tuple of classes accepted\n          as additional properties values.\n    ",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 15,
            "text": "    ApiTypeError,"
          },
          {
            "lineNumber": 16,
            "text": "    ModelComposed,"
          },
          {
            "lineNumber": 17,
            "text": "    ModelNormal,"
          },
          {
            "lineNumber": 18,
            "text": "    ModelSimple,"
          },
          {
            "lineNumber": 19,
            "text": "    cached_property,"
          },
          {
            "lineNumber": 20,
            "text": "    change_keys_js_to_python,"
          },
          {
            "lineNumber": 21,
            "text": "    convert_js_args_to_python_args,"
          },
          {
            "lineNumber": 22,
            "text": "    date,"
          },
          {
            "lineNumber": 23,
            "text": "    datetime,"
          },
          {
            "lineNumber": 24,
            "text": "    file_type,"
          },
          {
            "lineNumber": 25,
            "text": "    none_type,"
          },
          {
            "lineNumber": 26,
            "text": "    validate_get_composed_info,"
          },
          {
            "lineNumber": 27,
            "text": "    OpenApiModel"
          },
          {
            "lineNumber": 28,
            "text": ")"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31
          },
          {
            "isSignature": true,
            "lineNumber": 32,
            "text": "def lazy_import():"
          },
          {
            "lineNumber": 33,
            "text": "    from flamenco.manager.model.command import Command"
          },
          {
            "lineNumber": 34,
            "text": "    from flamenco.manager.model.task_status import TaskStatus"
          },
          {
            "lineNumber": 35,
            "text": "    from flamenco.manager.model.task_worker import TaskWorker"
          },
          {
            "lineNumber": 36,
            "text": "    globals()['Command'] = Command"
          },
          {
            "lineNumber": 37,
            "text": "    globals()['TaskStatus'] = TaskStatus"
          },
          {
            "isSignature": true,
            "lineNumber": 38,
            "text": "    globals()['TaskWorker'] = TaskWorker"
          },
          {
            "lineNumber": 39
          },
          {
            "lineNumber": 40
          },
          {
            "isSignature": true,
            "lineNumber": 41,
            "text": "class Task(ModelNormal):"
          },
          {
            "lineNumber": 42,
            "text": "    \"\"\"NOTE: This class is auto generated by OpenAPI Generator."
          },
          {
            "lineNumber": 43,
            "text": "    Ref: https://openapi-generator.tech"
          },
          {
            "lineNumber": 44
          },
          {
            "lineNumber": 45,
            "text": "    Do not edit the class manually."
          },
          {
            "lineNumber": 46
          },
          {
            "lineNumber": 47,
            "text": "    Attributes:"
          },
          {
            "lineNumber": 48,
            "text": "      allowed_values (dict): The key is the tuple path to the attribute"
          },
          {
            "lineNumber": 49,
            "text": "          and the for var_name this is (var_name,). The value is a dict"
          },
          {
            "lineNumber": 50,
            "text": "          with a capitalized key describing the allowed value and an allowed"
          },
          {
            "lineNumber": 51,
            "text": "          value. These dicts store the allowed enum values."
          },
          {
            "lineNumber": 52,
            "text": "      attribute_map (dict): The key is attribute name"
          },
          {
            "lineNumber": 53,
            "text": "          and the value is json key in definition."
          },
          {
            "lineNumber": 54,
            "text": "      discriminator_value_class_map (dict): A dict to go from the discriminator"
          },
          {
            "lineNumber": 55,
            "text": "          variable value to the discriminator class name."
          },
          {
            "lineNumber": 56,
            "text": "      validations (dict): The key is the tuple path to the attribute"
          },
          {
            "lineNumber": 57,
            "text": "          and the for var_name this is (var_name,). The value is a dict"
          },
          {
            "lineNumber": 58,
            "text": "          that stores validations for max_length, min_length, max_items,"
          },
          {
            "lineNumber": 59,
            "text": "          min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,"
          },
          {
            "lineNumber": 60,
            "text": "          inclusive_minimum, and regex."
          },
          {
            "lineNumber": 61,
            "text": "      additional_properties_type (tuple): A tuple of classes accepted"
          },
          {
            "lineNumber": 62,
            "text": "          as additional properties values."
          },
          {
            "lineNumber": 63,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 354
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\model\\task.py",
        "signatures": {}
      },
      "score": 0.34107035398483276
    },
    {
      "codeBlock": {
        "contents": "def register() -\u003e None:\n    from . import dependencies\n\n    dependencies.preload_modules()\n\n    bpy.app.handlers.load_pre.append(discard_global_flamenco_data)\n    bpy.app.handlers.load_factory_preferences_post.append(discard_global_flamenco_data)\n\n    bpy.app.handlers.load_post.append(_set_flamenco_job_name)\n    bpy.app.handlers.save_pre.append(_unset_flamenco_job_name)\n    bpy.app.handlers.save_post.append(_set_flamenco_job_name)\n\n    bpy.types.WindowManager.flamenco_bat_status = bpy.props.EnumProperty(\n        items=[\n            (\"IDLE\", \"IDLE\", \"Not doing anything.\"),\n            (\"SAVING\", \"SAVING\", \"Saving your file.\"),\n            (\"INVESTIGATING\", \"INVESTIGATING\", \"Finding all dependencies.\"),\n            (\"TRANSFERRING\", \"TRANSFERRING\", \"Transferring all dependencies.\"),\n            (\"COMMUNICATING\", \"COMMUNICATING\", \"Communicating with Flamenco Server.\"),\n            (\"DONE\", \"DONE\", \"Not doing anything, but doing something earlier.\"),\n            (\"ABORTING\", \"ABORTING\", \"User requested we stop doing something.\"),\n            (\"ABORTED\", \"ABORTED\", \"We stopped doing something.\"),\n        ],\n        name=\"flamenco_status\",\n        default=\"IDLE\",\n        description=\"Current status of the Flamenco add-on\",\n        update=redraw,\n    )\n\n    bpy.types.WindowManager.flamenco_bat_status_txt = bpy.props.StringProperty(\n        name=\"Flamenco Status\",\n        default=\"\",\n        description=\"Textual description of what Flamenco is doing\",\n        update=redraw,\n    )\n\n    bpy.types.WindowManager.flamenco_bat_progress = bpy.props.IntProperty(\n        name=\"Flamenco Progress\",\n        default=0,\n        description=\"File transfer progress\",\n        subtype=\"PERCENTAGE\",\n        min=0,\n        max=100,\n        update=redraw,\n    )\n    bpy.types.WindowManager.flamenco_version_mismatch = bpy.props.BoolProperty(\n        name=\"Flamenco Ignore Version Mismatch\",",
        "detailedLines": [
          {
            "isSignature": true,
            "lineNumber": 90,
            "text": "def register() -\u003e None:"
          },
          {
            "lineNumber": 91,
            "text": "    from . import dependencies"
          },
          {
            "lineNumber": 92
          },
          {
            "lineNumber": 93,
            "text": "    dependencies.preload_modules()"
          },
          {
            "lineNumber": 94
          },
          {
            "lineNumber": 95,
            "text": "    bpy.app.handlers.load_pre.append(discard_global_flamenco_data)"
          },
          {
            "lineNumber": 96,
            "text": "    bpy.app.handlers.load_factory_preferences_post.append(discard_global_flamenco_data)"
          },
          {
            "lineNumber": 97
          },
          {
            "lineNumber": 98,
            "text": "    bpy.app.handlers.load_post.append(_set_flamenco_job_name)"
          },
          {
            "lineNumber": 99,
            "text": "    bpy.app.handlers.save_pre.append(_unset_flamenco_job_name)"
          },
          {
            "lineNumber": 100,
            "text": "    bpy.app.handlers.save_post.append(_set_flamenco_job_name)"
          },
          {
            "lineNumber": 101
          },
          {
            "lineNumber": 102,
            "text": "    bpy.types.WindowManager.flamenco_bat_status = bpy.props.EnumProperty("
          },
          {
            "lineNumber": 103,
            "text": "        items=["
          },
          {
            "lineNumber": 104,
            "text": "            (\"IDLE\", \"IDLE\", \"Not doing anything.\"),"
          },
          {
            "lineNumber": 105,
            "text": "            (\"SAVING\", \"SAVING\", \"Saving your file.\"),"
          },
          {
            "lineNumber": 106,
            "text": "            (\"INVESTIGATING\", \"INVESTIGATING\", \"Finding all dependencies.\"),"
          },
          {
            "lineNumber": 107,
            "text": "            (\"TRANSFERRING\", \"TRANSFERRING\", \"Transferring all dependencies.\"),"
          },
          {
            "lineNumber": 108,
            "text": "            (\"COMMUNICATING\", \"COMMUNICATING\", \"Communicating with Flamenco Server.\"),"
          },
          {
            "lineNumber": 109,
            "text": "            (\"DONE\", \"DONE\", \"Not doing anything, but doing something earlier.\"),"
          },
          {
            "lineNumber": 110,
            "text": "            (\"ABORTING\", \"ABORTING\", \"User requested we stop doing something.\"),"
          },
          {
            "lineNumber": 111,
            "text": "            (\"ABORTED\", \"ABORTED\", \"We stopped doing something.\"),"
          },
          {
            "lineNumber": 112,
            "text": "        ],"
          },
          {
            "lineNumber": 113,
            "text": "        name=\"flamenco_status\","
          },
          {
            "lineNumber": 114,
            "text": "        default=\"IDLE\","
          },
          {
            "lineNumber": 115,
            "text": "        description=\"Current status of the Flamenco add-on\","
          },
          {
            "lineNumber": 116,
            "text": "        update=redraw,"
          },
          {
            "lineNumber": 117,
            "text": "    )"
          },
          {
            "lineNumber": 118
          },
          {
            "lineNumber": 119,
            "text": "    bpy.types.WindowManager.flamenco_bat_status_txt = bpy.props.StringProperty("
          },
          {
            "lineNumber": 120,
            "text": "        name=\"Flamenco Status\","
          },
          {
            "lineNumber": 121,
            "text": "        default=\"\","
          },
          {
            "lineNumber": 122,
            "text": "        description=\"Textual description of what Flamenco is doing\","
          },
          {
            "lineNumber": 123,
            "text": "        update=redraw,"
          },
          {
            "lineNumber": 124,
            "text": "    )"
          },
          {
            "lineNumber": 125
          },
          {
            "lineNumber": 126,
            "text": "    bpy.types.WindowManager.flamenco_bat_progress = bpy.props.IntProperty("
          },
          {
            "lineNumber": 127,
            "text": "        name=\"Flamenco Progress\","
          },
          {
            "lineNumber": 128,
            "text": "        default=0,"
          },
          {
            "lineNumber": 129,
            "text": "        description=\"File transfer progress\","
          },
          {
            "lineNumber": 130,
            "text": "        subtype=\"PERCENTAGE\","
          },
          {
            "lineNumber": 131,
            "text": "        min=0,"
          },
          {
            "lineNumber": 132,
            "text": "        max=100,"
          },
          {
            "lineNumber": 133,
            "text": "        update=redraw,"
          },
          {
            "lineNumber": 134,
            "text": "    )"
          },
          {
            "lineNumber": 135,
            "text": "    bpy.types.WindowManager.flamenco_version_mismatch = bpy.props.BoolProperty("
          },
          {
            "lineNumber": 136,
            "text": "        name=\"Flamenco Ignore Version Mismatch\","
          }
        ],
        "range": {
          "endPosition": {
            "line": 171
          },
          "startPosition": {
            "column": 32,
            "line": 86
          }
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\__init__.py",
        "signatures": {}
      },
      "score": 0.3397342562675476
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    ApiTypeError,\n    ModelComposed,\n    ModelNormal,\n    ModelSimple,\n    cached_property,\n    change_keys_js_to_python,\n    convert_js_args_to_python_args,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_get_composed_info,\n    OpenApiModel\n)\nfrom flamenco.manager.exceptions import ApiAttributeError\n\n\ndef lazy_import():\n    from flamenco.manager.model.command import Command\n    from flamenco.manager.model.task_status import TaskStatus\n    globals()['Command'] = Command\n    globals()['TaskStatus'] = TaskStatus\n\n\nclass AssignedTask(ModelNormal):\n    \"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 15,
            "text": "    ApiTypeError,"
          },
          {
            "lineNumber": 16,
            "text": "    ModelComposed,"
          },
          {
            "lineNumber": 17,
            "text": "    ModelNormal,"
          },
          {
            "lineNumber": 18,
            "text": "    ModelSimple,"
          },
          {
            "lineNumber": 19,
            "text": "    cached_property,"
          },
          {
            "lineNumber": 20,
            "text": "    change_keys_js_to_python,"
          },
          {
            "lineNumber": 21,
            "text": "    convert_js_args_to_python_args,"
          },
          {
            "lineNumber": 22,
            "text": "    date,"
          },
          {
            "lineNumber": 23,
            "text": "    datetime,"
          },
          {
            "lineNumber": 24,
            "text": "    file_type,"
          },
          {
            "lineNumber": 25,
            "text": "    none_type,"
          },
          {
            "lineNumber": 26,
            "text": "    validate_get_composed_info,"
          },
          {
            "lineNumber": 27,
            "text": "    OpenApiModel"
          },
          {
            "lineNumber": 28,
            "text": ")"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31
          },
          {
            "isSignature": true,
            "lineNumber": 32,
            "text": "def lazy_import():"
          },
          {
            "lineNumber": 33,
            "text": "    from flamenco.manager.model.command import Command"
          },
          {
            "lineNumber": 34,
            "text": "    from flamenco.manager.model.task_status import TaskStatus"
          },
          {
            "lineNumber": 35,
            "text": "    globals()['Command'] = Command"
          },
          {
            "isSignature": true,
            "lineNumber": 36,
            "text": "    globals()['TaskStatus'] = TaskStatus"
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38
          },
          {
            "isSignature": true,
            "lineNumber": 39,
            "text": "class AssignedTask(ModelNormal):"
          },
          {
            "lineNumber": 40,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 328
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\model\\assigned_task.py",
        "signatures": {}
      },
      "score": 0.33932793140411377
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    ApiTypeError,\n    ModelComposed,\n    ModelNormal,\n    ModelSimple,\n    cached_property,\n    change_keys_js_to_python,\n    convert_js_args_to_python_args,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_get_composed_info,\n    OpenApiModel\n)\nfrom flamenco.manager.exceptions import ApiAttributeError\n\n\n\nclass Command(ModelNormal):\n    \"\"\"NOTE: This class is auto generated by OpenAPI Generator.\n    Ref: https://openapi-generator.tech\n\n    Do not edit the class manually.\n\n    Attributes:\n      allowed_values (dict): The key is the tuple path to the attribute\n          and the for var_name this is (var_name,). The value is a dict\n          with a capitalized key describing the allowed value and an allowed\n          value. These dicts store the allowed enum values.\n      attribute_map (dict): The key is attribute name\n          and the value is json key in definition.\n      discriminator_value_class_map (dict): A dict to go from the discriminator\n          variable value to the discriminator class name.\n      validations (dict): The key is the tuple path to the attribute\n          and the for var_name this is (var_name,). The value is a dict\n          that stores validations for max_length, min_length, max_items,\n          min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,\n          inclusive_minimum, and regex.\n      additional_properties_type (tuple): A tuple of classes accepted\n          as additional properties values.\n    \"\"\"\n\n    allowed_values = {\n    }\n\n    validations = {\n    }\n\n    @cached_property\n    def additional_properties_type():\n        \"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 15,
            "text": "    ApiTypeError,"
          },
          {
            "lineNumber": 16,
            "text": "    ModelComposed,"
          },
          {
            "lineNumber": 17,
            "text": "    ModelNormal,"
          },
          {
            "lineNumber": 18,
            "text": "    ModelSimple,"
          },
          {
            "lineNumber": 19,
            "text": "    cached_property,"
          },
          {
            "lineNumber": 20,
            "text": "    change_keys_js_to_python,"
          },
          {
            "lineNumber": 21,
            "text": "    convert_js_args_to_python_args,"
          },
          {
            "lineNumber": 22,
            "text": "    date,"
          },
          {
            "lineNumber": 23,
            "text": "    datetime,"
          },
          {
            "lineNumber": 24,
            "text": "    file_type,"
          },
          {
            "lineNumber": 25,
            "text": "    none_type,"
          },
          {
            "lineNumber": 26,
            "text": "    validate_get_composed_info,"
          },
          {
            "lineNumber": 27,
            "text": "    OpenApiModel"
          },
          {
            "lineNumber": 28,
            "text": ")"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31
          },
          {
            "lineNumber": 32
          },
          {
            "isSignature": true,
            "lineNumber": 33,
            "text": "class Command(ModelNormal):"
          },
          {
            "lineNumber": 34,
            "text": "    \"\"\"NOTE: This class is auto generated by OpenAPI Generator."
          },
          {
            "lineNumber": 35,
            "text": "    Ref: https://openapi-generator.tech"
          },
          {
            "lineNumber": 36
          },
          {
            "lineNumber": 37,
            "text": "    Do not edit the class manually."
          },
          {
            "lineNumber": 38
          },
          {
            "lineNumber": 39,
            "text": "    Attributes:"
          },
          {
            "lineNumber": 40,
            "text": "      allowed_values (dict): The key is the tuple path to the attribute"
          },
          {
            "lineNumber": 41,
            "text": "          and the for var_name this is (var_name,). The value is a dict"
          },
          {
            "lineNumber": 42,
            "text": "          with a capitalized key describing the allowed value and an allowed"
          },
          {
            "lineNumber": 43,
            "text": "          value. These dicts store the allowed enum values."
          },
          {
            "lineNumber": 44,
            "text": "      attribute_map (dict): The key is attribute name"
          },
          {
            "lineNumber": 45,
            "text": "          and the value is json key in definition."
          },
          {
            "lineNumber": 46,
            "text": "      discriminator_value_class_map (dict): A dict to go from the discriminator"
          },
          {
            "lineNumber": 47,
            "text": "          variable value to the discriminator class name."
          },
          {
            "lineNumber": 48,
            "text": "      validations (dict): The key is the tuple path to the attribute"
          },
          {
            "lineNumber": 49,
            "text": "          and the for var_name this is (var_name,). The value is a dict"
          },
          {
            "lineNumber": 50,
            "text": "          that stores validations for max_length, min_length, max_items,"
          },
          {
            "lineNumber": 51,
            "text": "          min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,"
          },
          {
            "lineNumber": 52,
            "text": "          inclusive_minimum, and regex."
          },
          {
            "lineNumber": 53,
            "text": "      additional_properties_type (tuple): A tuple of classes accepted"
          },
          {
            "lineNumber": 54,
            "text": "          as additional properties values."
          },
          {
            "lineNumber": 55,
            "text": "    \"\"\""
          },
          {
            "lineNumber": 56
          },
          {
            "lineNumber": 57,
            "text": "    allowed_values = {"
          },
          {
            "lineNumber": 58,
            "text": "    }"
          },
          {
            "lineNumber": 59
          },
          {
            "lineNumber": 60,
            "text": "    validations = {"
          },
          {
            "lineNumber": 61,
            "text": "    }"
          },
          {
            "lineNumber": 62
          },
          {
            "lineNumber": 63,
            "text": "    @cached_property"
          },
          {
            "lineNumber": 64,
            "text": "    def additional_properties_type():"
          },
          {
            "lineNumber": 65,
            "text": "        \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 272
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\model\\command.py",
        "signatures": {}
      },
      "score": 0.3378849923610687
    },
    {
      "codeBlock": {
        "contents": "# flamenco.manager.ShamanApi\n\nAll URIs are relative to *http://localhost*\n\nMethod | HTTP request | Description\n------------- | ------------- | -------------\n[**shaman_checkout**](ShamanApi.md#shaman_checkout) | **POST** /api/v3/shaman/checkout/create | Create a directory, and symlink the required files into it. The files must all have been uploaded to Shaman before calling this endpoint.\n[**shaman_checkout_requirements**](ShamanApi.md#shaman_checkout_requirements) | **POST** /api/v3/shaman/checkout/requirements | Checks a Shaman Requirements file, and reports which files are unknown.\n[**shaman_file_store**](ShamanApi.md#shaman_file_store) | **POST** /api/v3/shaman/files/{checksum}/{filesize} | Store a new file on the Shaman server. Note that the Shaman server can forcibly close the HTTP connection when another client finishes uploading the exact same file, to prevent double uploads. The file\u0026#39;s contents should be sent in the request body. \n[**shaman_file_store_check**](ShamanApi.md#shaman_file_store_check) | **GET** /api/v3/shaman/files/{checksum}/{filesize} | Check the status of a file on the Shaman server. \n\n\n# **shaman_checkout**\n\u003e ShamanCheckoutResult shaman_checkout(shaman_checkout)\n\nCreate a directory, and symlink the required files into it. The files must all have been uploaded to Shaman before calling this endpoint.\n\n### Example\n\n\n&#96;&#96;&#96;python\nimport time\nimport flamenco.manager\nfrom flamenco.manager.api import shaman_api\nfrom flamenco.manager.model.error import Error\nfrom flamenco.manager.model.shaman_checkout import ShamanCheckout\nfrom flamenco.manager.model.shaman_checkout_result import ShamanCheckoutResult\nfrom pprint import pprint\n# Defining the host is optional and defaults to http://localhost\n# See configuration.py for a list of all supported configuration parameters.\nconfiguration = flamenco.manager.Configuration(\n    host = \"http://localhost\"\n)\n\n\n# Enter a context with an instance of the API client\nwith flamenco.manager.ApiClient() as api_client:\n    # Create an instance of the API class\n    api_instance = shaman_api.ShamanApi(api_client)",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# flamenco.manager.ShamanApi"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "All URIs are relative to *http://localhost*"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "Method | HTTP request | Description"
          },
          {
            "lineNumber": 6,
            "text": "------------- | ------------- | -------------"
          },
          {
            "lineNumber": 7,
            "text": "[**shaman_checkout**](ShamanApi.md#shaman_checkout) | **POST** /api/v3/shaman/checkout/create | Create a directory, and symlink the required files into it. The files must all have been uploaded to Shaman before calling this endpoint."
          },
          {
            "lineNumber": 8,
            "text": "[**shaman_checkout_requirements**](ShamanApi.md#shaman_checkout_requirements) | **POST** /api/v3/shaman/checkout/requirements | Checks a Shaman Requirements file, and reports which files are unknown."
          },
          {
            "lineNumber": 9,
            "text": "[**shaman_file_store**](ShamanApi.md#shaman_file_store) | **POST** /api/v3/shaman/files/{checksum}/{filesize} | Store a new file on the Shaman server. Note that the Shaman server can forcibly close the HTTP connection when another client finishes uploading the exact same file, to prevent double uploads. The file\u0026#39;s contents should be sent in the request body. "
          },
          {
            "lineNumber": 10,
            "text": "[**shaman_file_store_check**](ShamanApi.md#shaman_file_store_check) | **GET** /api/v3/shaman/files/{checksum}/{filesize} | Check the status of a file on the Shaman server. "
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "# **shaman_checkout**"
          },
          {
            "lineNumber": 14,
            "text": "\u003e ShamanCheckoutResult shaman_checkout(shaman_checkout)"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "Create a directory, and symlink the required files into it. The files must all have been uploaded to Shaman before calling this endpoint."
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "### Example"
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21,
            "text": "&#96;&#96;&#96;python"
          },
          {
            "lineNumber": 22,
            "text": "import time"
          },
          {
            "lineNumber": 23,
            "text": "import flamenco.manager"
          },
          {
            "lineNumber": 24,
            "text": "from flamenco.manager.api import shaman_api"
          },
          {
            "lineNumber": 25,
            "text": "from flamenco.manager.model.error import Error"
          },
          {
            "lineNumber": 26,
            "text": "from flamenco.manager.model.shaman_checkout import ShamanCheckout"
          },
          {
            "lineNumber": 27,
            "text": "from flamenco.manager.model.shaman_checkout_result import ShamanCheckoutResult"
          },
          {
            "lineNumber": 28,
            "text": "from pprint import pprint"
          },
          {
            "lineNumber": 29,
            "text": "# Defining the host is optional and defaults to http://localhost"
          },
          {
            "lineNumber": 30,
            "text": "# See configuration.py for a list of all supported configuration parameters."
          },
          {
            "lineNumber": 31,
            "text": "configuration = flamenco.manager.Configuration("
          },
          {
            "lineNumber": 32,
            "text": "    host = \"http://localhost\""
          },
          {
            "lineNumber": 33,
            "text": ")"
          },
          {
            "lineNumber": 34
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36,
            "text": "# Enter a context with an instance of the API client"
          },
          {
            "lineNumber": 37,
            "text": "with flamenco.manager.ApiClient() as api_client:"
          },
          {
            "lineNumber": 38,
            "text": "    # Create an instance of the API class"
          },
          {
            "lineNumber": 39,
            "text": "    api_instance = shaman_api.ShamanApi(api_client)"
          }
        ],
        "range": {
          "endPosition": {
            "line": 39
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\docs\\ShamanApi.md",
        "signatures": {}
      },
      "score": 0.3369028568267822
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    ApiTypeError,\n    ModelComposed,\n    ModelNormal,\n    ModelSimple,\n    cached_property,\n    change_keys_js_to_python,\n    convert_js_args_to_python_args,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_get_composed_info,\n    OpenApiModel\n)\nfrom flamenco.manager.exceptions import ApiAttributeError\n\n\ndef lazy_import():\n    from flamenco.manager.model.manager_variable_audience import ManagerVariableAudience\n    globals()['ManagerVariableAudience'] = ManagerVariableAudience\n\n\nclass SharedStorageLocation(ModelNormal):\n    \"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 15,
            "text": "    ApiTypeError,"
          },
          {
            "lineNumber": 16,
            "text": "    ModelComposed,"
          },
          {
            "lineNumber": 17,
            "text": "    ModelNormal,"
          },
          {
            "lineNumber": 18,
            "text": "    ModelSimple,"
          },
          {
            "lineNumber": 19,
            "text": "    cached_property,"
          },
          {
            "lineNumber": 20,
            "text": "    change_keys_js_to_python,"
          },
          {
            "lineNumber": 21,
            "text": "    convert_js_args_to_python_args,"
          },
          {
            "lineNumber": 22,
            "text": "    date,"
          },
          {
            "lineNumber": 23,
            "text": "    datetime,"
          },
          {
            "lineNumber": 24,
            "text": "    file_type,"
          },
          {
            "lineNumber": 25,
            "text": "    none_type,"
          },
          {
            "lineNumber": 26,
            "text": "    validate_get_composed_info,"
          },
          {
            "lineNumber": 27,
            "text": "    OpenApiModel"
          },
          {
            "lineNumber": 28,
            "text": ")"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31
          },
          {
            "isSignature": true,
            "lineNumber": 32,
            "text": "def lazy_import():"
          },
          {
            "lineNumber": 33,
            "text": "    from flamenco.manager.model.manager_variable_audience import ManagerVariableAudience"
          },
          {
            "isSignature": true,
            "lineNumber": 34,
            "text": "    globals()['ManagerVariableAudience'] = ManagerVariableAudience"
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36
          },
          {
            "isSignature": true,
            "lineNumber": 37,
            "text": "class SharedStorageLocation(ModelNormal):"
          },
          {
            "lineNumber": 38,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 284
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\model\\shared_storage_location.py",
        "signatures": {}
      },
      "score": 0.3361254930496216
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    ApiTypeError,\n    ModelComposed,\n    ModelNormal,\n    ModelSimple,\n    cached_property,\n    change_keys_js_to_python,\n    convert_js_args_to_python_args,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_get_composed_info,\n    OpenApiModel\n)\nfrom flamenco.manager.exceptions import ApiAttributeError\n\n\ndef lazy_import():\n    from flamenco.manager.model.job_all_of import JobAllOf\n    from flamenco.manager.model.job_metadata import JobMetadata\n    from flamenco.manager.model.job_settings import JobSettings\n    from flamenco.manager.model.job_status import JobStatus\n    from flamenco.manager.model.job_storage_info import JobStorageInfo\n    from flamenco.manager.model.submitted_job import SubmittedJob\n    globals()['JobAllOf'] = JobAllOf\n    globals()['JobMetadata'] = JobMetadata\n    globals()['JobSettings'] = JobSettings\n    globals()['JobStatus'] = JobStatus\n    globals()['JobStorageInfo'] = JobStorageInfo\n    globals()['SubmittedJob'] = SubmittedJob\n\n\nclass Job(ModelComposed):\n    \"\"\"NOTE: This class is auto generated by OpenAPI Generator.\n    Ref: https://openapi-generator.tech\n\n    Do not edit the class manually.\n\n    Attributes:\n      allowed_values (dict): The key is the tuple path to the attribute\n          and the for var_name this is (var_name,). The value is a dict\n          with a capitalized key describing the allowed value and an allowed\n          value. These dicts store the allowed enum values.\n      attribute_map (dict): The key is attribute name\n          and the value is json key in definition.\n      discriminator_value_class_map (dict): A dict to go from the discriminator\n          variable value to the discriminator class name.\n      validations (dict): The key is the tuple path to the attribute\n          and the for var_name this is (var_name,). The value is a dict\n          that stores validations for max_length, min_length, max_items,\n          min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,\n          inclusive_minimum, and regex.\n      additional_properties_type (tuple): A tuple of classes accepted\n          as additional properties values.\n    ",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 15,
            "text": "    ApiTypeError,"
          },
          {
            "lineNumber": 16,
            "text": "    ModelComposed,"
          },
          {
            "lineNumber": 17,
            "text": "    ModelNormal,"
          },
          {
            "lineNumber": 18,
            "text": "    ModelSimple,"
          },
          {
            "lineNumber": 19,
            "text": "    cached_property,"
          },
          {
            "lineNumber": 20,
            "text": "    change_keys_js_to_python,"
          },
          {
            "lineNumber": 21,
            "text": "    convert_js_args_to_python_args,"
          },
          {
            "lineNumber": 22,
            "text": "    date,"
          },
          {
            "lineNumber": 23,
            "text": "    datetime,"
          },
          {
            "lineNumber": 24,
            "text": "    file_type,"
          },
          {
            "lineNumber": 25,
            "text": "    none_type,"
          },
          {
            "lineNumber": 26,
            "text": "    validate_get_composed_info,"
          },
          {
            "lineNumber": 27,
            "text": "    OpenApiModel"
          },
          {
            "lineNumber": 28,
            "text": ")"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31
          },
          {
            "isSignature": true,
            "lineNumber": 32,
            "text": "def lazy_import():"
          },
          {
            "lineNumber": 33,
            "text": "    from flamenco.manager.model.job_all_of import JobAllOf"
          },
          {
            "lineNumber": 34,
            "text": "    from flamenco.manager.model.job_metadata import JobMetadata"
          },
          {
            "lineNumber": 35,
            "text": "    from flamenco.manager.model.job_settings import JobSettings"
          },
          {
            "lineNumber": 36,
            "text": "    from flamenco.manager.model.job_status import JobStatus"
          },
          {
            "lineNumber": 37,
            "text": "    from flamenco.manager.model.job_storage_info import JobStorageInfo"
          },
          {
            "lineNumber": 38,
            "text": "    from flamenco.manager.model.submitted_job import SubmittedJob"
          },
          {
            "lineNumber": 39,
            "text": "    globals()['JobAllOf'] = JobAllOf"
          },
          {
            "lineNumber": 40,
            "text": "    globals()['JobMetadata'] = JobMetadata"
          },
          {
            "lineNumber": 41,
            "text": "    globals()['JobSettings'] = JobSettings"
          },
          {
            "lineNumber": 42,
            "text": "    globals()['JobStatus'] = JobStatus"
          },
          {
            "lineNumber": 43,
            "text": "    globals()['JobStorageInfo'] = JobStorageInfo"
          },
          {
            "isSignature": true,
            "lineNumber": 44,
            "text": "    globals()['SubmittedJob'] = SubmittedJob"
          },
          {
            "lineNumber": 45
          },
          {
            "lineNumber": 46
          },
          {
            "isSignature": true,
            "lineNumber": 47,
            "text": "class Job(ModelComposed):"
          },
          {
            "lineNumber": 48,
            "text": "    \"\"\"NOTE: This class is auto generated by OpenAPI Generator."
          },
          {
            "lineNumber": 49,
            "text": "    Ref: https://openapi-generator.tech"
          },
          {
            "lineNumber": 50
          },
          {
            "lineNumber": 51,
            "text": "    Do not edit the class manually."
          },
          {
            "lineNumber": 52
          },
          {
            "lineNumber": 53,
            "text": "    Attributes:"
          },
          {
            "lineNumber": 54,
            "text": "      allowed_values (dict): The key is the tuple path to the attribute"
          },
          {
            "lineNumber": 55,
            "text": "          and the for var_name this is (var_name,). The value is a dict"
          },
          {
            "lineNumber": 56,
            "text": "          with a capitalized key describing the allowed value and an allowed"
          },
          {
            "lineNumber": 57,
            "text": "          value. These dicts store the allowed enum values."
          },
          {
            "lineNumber": 58,
            "text": "      attribute_map (dict): The key is attribute name"
          },
          {
            "lineNumber": 59,
            "text": "          and the value is json key in definition."
          },
          {
            "lineNumber": 60,
            "text": "      discriminator_value_class_map (dict): A dict to go from the discriminator"
          },
          {
            "lineNumber": 61,
            "text": "          variable value to the discriminator class name."
          },
          {
            "lineNumber": 62,
            "text": "      validations (dict): The key is the tuple path to the attribute"
          },
          {
            "lineNumber": 63,
            "text": "          and the for var_name this is (var_name,). The value is a dict"
          },
          {
            "lineNumber": 64,
            "text": "          that stores validations for max_length, min_length, max_items,"
          },
          {
            "lineNumber": 65,
            "text": "          min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,"
          },
          {
            "lineNumber": 66,
            "text": "          inclusive_minimum, and regex."
          },
          {
            "lineNumber": 67,
            "text": "      additional_properties_type (tuple): A tuple of classes accepted"
          },
          {
            "lineNumber": 68,
            "text": "          as additional properties values."
          },
          {
            "lineNumber": 69,
            "text": "    "
          }
        ],
        "range": {
          "endPosition": {
            "line": 391
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\model\\job.py",
        "signatures": {}
      },
      "score": 0.3360063135623932
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    ApiTypeError,\n    ModelComposed,\n    ModelNormal,\n    ModelSimple,\n    cached_property,\n    change_keys_js_to_python,\n    convert_js_args_to_python_args,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_get_composed_info,\n    OpenApiModel\n)\nfrom flamenco.manager.exceptions import ApiAttributeError\n\n\ndef lazy_import():\n    from flamenco.manager.model.job_metadata import JobMetadata\n    from flamenco.manager.model.job_settings import JobSettings\n    from flamenco.manager.model.job_status import JobStatus\n    from flamenco.manager.model.job_storage_info import JobStorageInfo\n    globals()['JobMetadata'] = JobMetadata\n    globals()['JobSettings'] = JobSettings\n    globals()['JobStatus'] = JobStatus\n    globals()['JobStorageInfo'] = JobStorageInfo\n\n\nclass SubmittedJob(ModelNormal):\n    \"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 15,
            "text": "    ApiTypeError,"
          },
          {
            "lineNumber": 16,
            "text": "    ModelComposed,"
          },
          {
            "lineNumber": 17,
            "text": "    ModelNormal,"
          },
          {
            "lineNumber": 18,
            "text": "    ModelSimple,"
          },
          {
            "lineNumber": 19,
            "text": "    cached_property,"
          },
          {
            "lineNumber": 20,
            "text": "    change_keys_js_to_python,"
          },
          {
            "lineNumber": 21,
            "text": "    convert_js_args_to_python_args,"
          },
          {
            "lineNumber": 22,
            "text": "    date,"
          },
          {
            "lineNumber": 23,
            "text": "    datetime,"
          },
          {
            "lineNumber": 24,
            "text": "    file_type,"
          },
          {
            "lineNumber": 25,
            "text": "    none_type,"
          },
          {
            "lineNumber": 26,
            "text": "    validate_get_composed_info,"
          },
          {
            "lineNumber": 27,
            "text": "    OpenApiModel"
          },
          {
            "lineNumber": 28,
            "text": ")"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.exceptions import ApiAttributeError"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31
          },
          {
            "isSignature": true,
            "lineNumber": 32,
            "text": "def lazy_import():"
          },
          {
            "lineNumber": 33,
            "text": "    from flamenco.manager.model.job_metadata import JobMetadata"
          },
          {
            "lineNumber": 34,
            "text": "    from flamenco.manager.model.job_settings import JobSettings"
          },
          {
            "lineNumber": 35,
            "text": "    from flamenco.manager.model.job_status import JobStatus"
          },
          {
            "lineNumber": 36,
            "text": "    from flamenco.manager.model.job_storage_info import JobStorageInfo"
          },
          {
            "lineNumber": 37,
            "text": "    globals()['JobMetadata'] = JobMetadata"
          },
          {
            "lineNumber": 38,
            "text": "    globals()['JobSettings'] = JobSettings"
          },
          {
            "lineNumber": 39,
            "text": "    globals()['JobStatus'] = JobStatus"
          },
          {
            "isSignature": true,
            "lineNumber": 40,
            "text": "    globals()['JobStorageInfo'] = JobStorageInfo"
          },
          {
            "lineNumber": 41
          },
          {
            "lineNumber": 42
          },
          {
            "isSignature": true,
            "lineNumber": 43,
            "text": "class SubmittedJob(ModelNormal):"
          },
          {
            "lineNumber": 44,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 316
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\model\\submitted_job.py",
        "signatures": {}
      },
      "score": 0.33388200402259827
    },
    {
      "codeBlock": {
        "contents": "# SPDX-License-Identifier: GPL-3.0-or-later\n\"\"\"BAT packing interface for Flamenco.\"\"\"\n\nfrom dataclasses import dataclass\nfrom pathlib import Path, PurePosixPath\nfrom typing import Optional, Any\nimport logging\nimport queue\nimport threading\nimport typing\n\nfrom . import submodules\n\nlog = logging.getLogger(__name__)\n\n# # For using in other parts of the add-on, so only this file imports BAT.\n# Aborted = pack.Aborted\n# FileTransferError = transfer.FileTransferError\n# parse_shaman_endpoint = shaman.parse_endpoint\n\n\nclass Message:\n    \"\"\"Superclass for message objects queued by the BatProgress class.\"\"\"\n\n\n@dataclass\nclass MsgSetWMAttribute(Message):\n    \"\"\"Set a WindowManager attribute to a value.\"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 2,
            "text": "\"\"\"BAT packing interface for Flamenco.\"\"\""
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "from dataclasses import dataclass"
          },
          {
            "lineNumber": 5,
            "text": "from pathlib import Path, PurePosixPath"
          },
          {
            "lineNumber": 6,
            "text": "from typing import Optional, Any"
          },
          {
            "lineNumber": 7,
            "text": "import logging"
          },
          {
            "lineNumber": 8,
            "text": "import queue"
          },
          {
            "lineNumber": 9,
            "text": "import threading"
          },
          {
            "lineNumber": 10,
            "text": "import typing"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "from . import submodules"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "log = logging.getLogger(__name__)"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "# # For using in other parts of the add-on, so only this file imports BAT."
          },
          {
            "lineNumber": 17,
            "text": "# Aborted = pack.Aborted"
          },
          {
            "lineNumber": 18,
            "text": "# FileTransferError = transfer.FileTransferError"
          },
          {
            "lineNumber": 19,
            "text": "# parse_shaman_endpoint = shaman.parse_endpoint"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21
          },
          {
            "isSignature": true,
            "lineNumber": 22,
            "text": "class Message:"
          },
          {
            "isSignature": true,
            "lineNumber": 23,
            "text": "    \"\"\"Superclass for message objects queued by the BatProgress class.\"\"\""
          },
          {
            "lineNumber": 24
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "@dataclass"
          },
          {
            "isSignature": true,
            "lineNumber": 27,
            "text": "class MsgSetWMAttribute(Message):"
          },
          {
            "lineNumber": 28,
            "text": "    \"\"\"Set a WindowManager attribute to a value.\"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 30
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\bat\\interface.py",
        "signatures": {}
      },
      "score": 0.3322540521621704
    },
    {
      "codeBlock": {
        "contents": "\"\"\"\n    Flamenco manager\n\n    Render Farm manager API  # noqa: E501\n\n    The version of the OpenAPI document: 1.0.0\n    Generated by: https://openapi-generator.tech\n\"\"\"\n\n\nimport re  # noqa: F401\nimport sys  # noqa: F401\n\nfrom flamenco.manager.api_client import ApiClient, Endpoint as _Endpoint\nfrom flamenco.manager.model_utils import (  # noqa: F401\n    check_allowed_values,\n    check_validations,\n    date,\n    datetime,\n    file_type,\n    none_type,\n    validate_and_convert_types\n)\nfrom flamenco.manager.model.blender_path_check_result import BlenderPathCheckResult\nfrom flamenco.manager.model.blender_path_find_result import BlenderPathFindResult\nfrom flamenco.manager.model.error import Error\nfrom flamenco.manager.model.farm_status_report import FarmStatusReport\nfrom flamenco.manager.model.flamenco_version import FlamencoVersion\nfrom flamenco.manager.model.manager_configuration import ManagerConfiguration\nfrom flamenco.manager.model.manager_variable_audience import ManagerVariableAudience\nfrom flamenco.manager.model.manager_variables import ManagerVariables\nfrom flamenco.manager.model.path_check_input import PathCheckInput\nfrom flamenco.manager.model.path_check_result import PathCheckResult\nfrom flamenco.manager.model.setup_assistant_config import SetupAssistantConfig\nfrom flamenco.manager.model.shared_storage_location import SharedStorageLocation\n\n\nclass MetaApi(object):\n    \"\"\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "\"\"\""
          },
          {
            "lineNumber": 2,
            "text": "    Flamenco manager"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "    Render Farm manager API  # noqa: E501"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "    The version of the OpenAPI document: 1.0.0"
          },
          {
            "lineNumber": 7,
            "text": "    Generated by: https://openapi-generator.tech"
          },
          {
            "lineNumber": 8,
            "text": "\"\"\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "import re  # noqa: F401"
          },
          {
            "lineNumber": 12,
            "text": "import sys  # noqa: F401"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "from flamenco.manager.api_client import ApiClient, Endpoint as _Endpoint"
          },
          {
            "lineNumber": 15,
            "text": "from flamenco.manager.model_utils import (  # noqa: F401"
          },
          {
            "lineNumber": 16,
            "text": "    check_allowed_values,"
          },
          {
            "lineNumber": 17,
            "text": "    check_validations,"
          },
          {
            "lineNumber": 18,
            "text": "    date,"
          },
          {
            "lineNumber": 19,
            "text": "    datetime,"
          },
          {
            "lineNumber": 20,
            "text": "    file_type,"
          },
          {
            "lineNumber": 21,
            "text": "    none_type,"
          },
          {
            "lineNumber": 22,
            "text": "    validate_and_convert_types"
          },
          {
            "lineNumber": 23,
            "text": ")"
          },
          {
            "lineNumber": 24,
            "text": "from flamenco.manager.model.blender_path_check_result import BlenderPathCheckResult"
          },
          {
            "lineNumber": 25,
            "text": "from flamenco.manager.model.blender_path_find_result import BlenderPathFindResult"
          },
          {
            "lineNumber": 26,
            "text": "from flamenco.manager.model.error import Error"
          },
          {
            "lineNumber": 27,
            "text": "from flamenco.manager.model.farm_status_report import FarmStatusReport"
          },
          {
            "lineNumber": 28,
            "text": "from flamenco.manager.model.flamenco_version import FlamencoVersion"
          },
          {
            "lineNumber": 29,
            "text": "from flamenco.manager.model.manager_configuration import ManagerConfiguration"
          },
          {
            "lineNumber": 30,
            "text": "from flamenco.manager.model.manager_variable_audience import ManagerVariableAudience"
          },
          {
            "lineNumber": 31,
            "text": "from flamenco.manager.model.manager_variables import ManagerVariables"
          },
          {
            "lineNumber": 32,
            "text": "from flamenco.manager.model.path_check_input import PathCheckInput"
          },
          {
            "lineNumber": 33,
            "text": "from flamenco.manager.model.path_check_result import PathCheckResult"
          },
          {
            "lineNumber": 34,
            "text": "from flamenco.manager.model.setup_assistant_config import SetupAssistantConfig"
          },
          {
            "lineNumber": 35,
            "text": "from flamenco.manager.model.shared_storage_location import SharedStorageLocation"
          },
          {
            "lineNumber": 36
          },
          {
            "lineNumber": 37
          },
          {
            "isSignature": true,
            "lineNumber": 38,
            "text": "class MetaApi(object):"
          },
          {
            "lineNumber": 39,
            "text": "    \"\"\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 1380
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\flamenco\\manager\\api\\meta_api.py",
        "signatures": {}
      },
      "score": 0.33054763078689575
    }
  ]
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full"><details>
<summary>Tool use: **semantic_search_full**</summary>

</details></tool-use>

_**Agent (default)**_

Checking VSCode settings and researching Blender addon extension configuration:


_**Agent (default)**_

<tool-use data-tool-type="search" data-tool-name="glob_file_search">
<details>
<summary>Tool use: **glob_file_search** • Searched codebase "" • **0** results in **1** directory</summary>

Directory: **** (0 files)

</details>
</tool-use>

<tool-use data-tool-type="search" data-tool-name="glob_file_search"><details>
<summary>Tool use: **glob_file_search**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\flamenco\__init__.py</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="web_search">
<details>
<summary>Tool use: **web_search**</summary>


Parameters:

```json
{
  "searchTerm": "Blender addon VSCode extension configuration addon path nested folder"
}
```
Result:

```json
{
  "isFinal": true,
  "references": [
    {
      "chunk": "Links:\n1. [Visual Studio Code for Add-on Development ¶](https://developer.blender.org/docs/handbook/extensions/addon_dev_vscode)\n2. [How to Configure Visual Studio Code for Blender Add-on Creation| Sabbirz | Blog](https://www.sabbirz.com/blog/how-to-configure-visual-studio-code-for-blender-ad)\n3. [Blender Development](https://marketplace.visualstudio.com/items?itemName=JacquesLucke.blender-development)\n4. [Visual Studio Code ¶](https://developer.blender.org/docs/handbook/development_environments/vscode/)\n5. [Blender Development (Fork)](https://marketplace.visualstudio.com/items?itemName=zqchen.blender-development-fork)\n\n# VS Code Configuration for Blender Addons in Nested Folders\n\n## Extension Setup\nInstall the **Blender Development extension** (id: &#96;JacquesLucke.blender-development&#96;) from the VS Code marketplace.[2][3] This extension handles addon management and can work with addons in nested folder structures.\n\n## Addon Path Configuration\nThe Blender Development extension creates permanent soft links (or junctions on Windows) to connect your workspace to Blender's addon directory.[5] For legacy addons, it links to &#96;bpy.utils.user_resource(\"SCRIPTS\", path=\"addons\")&#96;, and for extensions, to &#96;bpy.utils.user_resource(\"EXTENSIONS\", path=\"vscode_development\")&#96;.[5]\n\n**For nested folders**: The extension supports addons with folder structure. If your addon is organized in nested directories, simply ensure your project has an &#96;__init__.py&#96; file in the root addon folder.[5] Open the addon folder in VS Code and use the &#96;Blender: Start&#96; command.[3]\n\n## Workspace Settings for Type Checking\nTo configure Pylance's type checking behavior, create a &#96;.vscode/settings.json&#96; file in your project root by opening the Command Palette (&#96;Ctrl+Shift+P&#96;) and selecting \"Open Workspace Settings (JSON)\".[1] Add this to disable type annotation warnings:\n\n&#96;&#96;&#96;json\n\"python.analysis.diagnosticSeverityOverrides\": {\n  \"reportInvalidTypeForm\": \"none\"\n}\n&#96;&#96;&#96;\n\nThis prevents conflicts with Blender's custom property registration system.[1]\n\n## Basic Workflow\n1. Open your addon folder in VS Code\n2. Press &#96;Ctrl+Shift+P&#96; and run &#96;Blender: Start&#96;\n3. Select a Blender version\n4. Edit files and use &#96;Blender: Reload Addons&#96; to test changes[3]",
      "title": "Web search results for query: Blender addon VSCode extension configuration addon path nested folder"
    }
  ],
  "rejected": false
}
```
User decision: **accepted**

Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="web_search"><details>
<summary>Tool use: **web_search**</summary>

</details></tool-use>

_**Agent (default)**_

Yes, you can use the Blender addon VSCode extension with your nested structure. The extension detects the addon by finding `__init__.py` with `bl_info`, which you have at `addon\flamenco\__init__.py`.

Options:
1. Open the `addon` folder as your workspace root (recommended)
2. Open the `addon\flamenco` folder directly

Checking if we need to create a workspace configuration:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\.vscode\extensions.json</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="list_dir_v2">
<details>
<summary>Tool use: **list_dir_v2**</summary>


Parameters:

```json
{
  "ignoreGlobs": [
    "__pycache__"
  ],
  "targetDirectory": "e:\\flamenco\\addon\\flamenco"
}
```
Additional data:

```json
{
  "extensionCounts": {
    ".py": 80
  },
  "isPruned": true,
  "rootPath": "e:\\flamenco\\addon\\flamenco",
  "topEntries": [
    {
      "isDir": true,
      "numFiles": 0,
      "path": "e:\\flamenco\\addon\\flamenco\\bat"
    },
    {
      "isDir": true,
      "numFiles": 80,
      "path": "e:\\flamenco\\addon\\flamenco\\manager"
    },
    {
      "isDir": true,
      "numFiles": 0,
      "path": "e:\\flamenco\\addon\\flamenco\\wheels"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/__init__.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/comms.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/dependencies.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/gui.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/job_submission.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/job_types_propgroup.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/job_types.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/manager_info.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/manager_README.md"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/operators.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/preferences.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/projects.py"
    },
    {
      "isDir": false,
      "path": "e:\\flamenco\\addon\\flamenco/worker_tags.py"
    }
  ],
  "totalDirs": 8,
  "totalFiles": 141
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="list_dir_v2"><details>
<summary>Tool use: **list_dir_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Adding the Blender Development extension to recommendations and creating a workspace settings file to configure it for the nested addon structure:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\.vscode\extensions.json</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="search" data-tool-name="glob_file_search">
<details>
<summary>Tool use: **glob_file_search** • Searched codebase "" • **0** results in **1** directory</summary>

Directory: **** (0 files)

</details>
</tool-use>

<tool-use data-tool-type="search" data-tool-name="glob_file_search"><details>
<summary>Tool use: **glob_file_search**</summary>

</details></tool-use>

_**Agent (default)**_

Adding the Blender Development extension to recommendations and creating a settings file for the nested addon structure:


_**Agent (default)**_

<tool-use data-tool-type="write" data-tool-name="edit_file_v2">

<details><summary>Tool use: **edit_file_v2** • Edit file: e:\flamenco\.vscode\extensions.json</summary>

</details>

</tool-use>

<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
<summary>Tool use: **edit_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="write" data-tool-name="edit_file_v2">

<details><summary>Tool use: **edit_file_v2** • Edit file: e:\flamenco\.vscode\settings.json</summary>

</details>

</tool-use>

<tool-use data-tool-type="write" data-tool-name="edit_file_v2"><details>
<summary>Tool use: **edit_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Configured VSCode for the Blender addon extension. Here's how to use it:

## Setup

1. Install the extension: VSCode should prompt you to install "Blender Development" (JacquesLucke.blender-development). If not, install it manually.

2. Using the nested structure: The extension can work with your `addon\flamenco\` structure. You have two options:

   Option A: Keep your current workspace (recommended)
   - Stay in the root `e:\flamenco` workspace
   - The extension should auto-detect the addon at `addon\flamenco\` when you run `Blender: Start`
   - It looks for `__init__.py` with `bl_info`, which you have

   Option B: Open the addon folder directly
   - Open `e:\flamenco\addon` as your workspace root
   - The extension will find `flamenco\__init__.py` directly

## Usage

1. Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
2. Run `Blender: Start`
3. Select your Blender version
4. Edit files in `addon\flamenco\`
5. Use `Blender: Reload Addons` to test changes without restarting Blender

The extension creates a symlink/junction from Blender's addons directory to your workspace, so it works with nested folders as long as it finds the `__init__.py` with `bl_info`.

I've also added Python analysis settings to suppress type annotation warnings that can occur with Blender's custom property system.

---

_**User**_

@addon/README.md:1-32 does sybren use a different tool?

---

_**Agent (default)**_

Checking for other development tool configurations or references:


_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search">
<details>
<summary>Tool use: **ripgrep_raw_search**</summary>


Parameters:

```json
{
  "caseInsensitive": true,
  "path": "e:\\flamenco",
  "pattern": "vscode|VSCode|IDE|editor|pycharm|vim|emacs"
}
```
Additional data:

```json
{
  "isPruned": true,
  "outputMode": "content",
  "path": "e:\\flamenco",
  "pattern": "vscode|VSCode|IDE|editor|pycharm|vim|emacs",
  "topFiles": [
    {
      "matchCount": 5,
      "uri": "web/app/.gitignore"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/api_impl/timeouts.go"
    },
    {
      "matchCount": 1,
      "uri": "web/project-website/content/usage/variables/blender.md"
    },
    {
      "matchCount": 2,
      "uri": "internal/own_url/own_url.go"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/eventbus/events_farmstatus.go"
    },
    {
      "matchCount": 2,
      "uri": ".vscode/extensions.json"
    },
    {
      "matchCount": 1,
      "uri": "internal/worker/persistence/upstream_buffer_test.go"
    },
    {
      "matchCount": 1,
      "uri": "pkg/shaman/checkout/report_requirements.go"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/api_impl/dummy/shaman.go"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/job_compilers/js_globals_test.go"
    },
    {
      "matchCount": 1,
      "uri": "pkg/duration/time_duration_test.go"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/persistence/sqlite_busy.go"
    },
    {
      "matchCount": 1,
      "uri": "pkg/api/openapi_server.gen.go"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/sleep_scheduler/interfaces.go"
    },
    {
      "matchCount": 1,
      "uri": "internal/manager/persistence/logger.go"
    },
    {
      "matchCount": 2,
      "uri": "cmd/mqtt-server/hook.go"
    },
    {
      "matchCount": 2,
      "uri": "web/app/src/components/TabsWrapper.vue"
    },
    {
      "matchCount": 4,
      "uri": "web/app/src/components/jobs/JobActionsBar.vue"
    },
    {
      "matchCount": 1,
      "uri": "addon/flamenco/worker_tags.py"
    },
    {
      "matchCount": 1,
      "uri": "internal/find_blender/darwin.go"
    }
  ],
  "totalFiles": 353,
  "totalMatches": 1507
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="ripgrep_raw_search"><details>
<summary>Tool use: **ripgrep_raw_search**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\addon\README.md</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full">
<details>
<summary>Tool use: **semantic_search_full**</summary>


Parameters:

```json
{
  "explanation": "",
  "query": "What development tools or IDE configurations are used for this project?",
  "topK": 10
}
```
Result:

```json
{
  "codeResults": [
    {
      "codeBlock": {
        "contents": "{\n  \"name\": \"flamenco-manager\",\n  \"version\": \"0.0.3\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"Sybren A. Stüvel\"\n  },\n  \"licenses\": [\n    {\n      \"type\": \"GPL-3.0\",\n      \"url\": \"https://opensource.org/licenses/GPL-3.0\"\n    }\n  ],\n  \"scripts\": {\n    \"dev\": \"vite --port 8081 --base /app/ --mode development\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview --port 5050\",\n    \"lint\": \"eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore\"\n  },\n  \"dependencies\": {\n    \"luxon\": \"^2.3.1\",\n    \"microtip\": \"^0.2.2\",\n    \"pinia\": \"^2.0.13\",\n    \"socket.io-client\": \"2\",\n    \"superagent\": \"^7.1.2\",\n    \"tabulator-tables\": \"^6.3.0\",\n    \"vue\": \"^3.2.31\",\n    \"vue-router\": \"^4.0.14\"\n  },\n  \"devDependencies\": {\n    \"@rushstack/eslint-patch\": \"^1.1.0\",\n    \"@vitejs/plugin-vue\": \"^2.3.1\",\n    \"@vue/eslint-config-prettier\": \"^7.0.0\",\n    \"eslint\": \"^8.5.0\",\n    \"eslint-plugin-vue\": \"^8.2.0\",\n    \"prettier\": \"^2.5.1\",\n    \"vite\": \"^2.9.1\"\n  }\n}",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "{"
          },
          {
            "lineNumber": 2,
            "text": "  \"name\": \"flamenco-manager\","
          },
          {
            "lineNumber": 3,
            "text": "  \"version\": \"0.0.3\","
          },
          {
            "lineNumber": 4,
            "text": "  \"private\": true,"
          },
          {
            "lineNumber": 5,
            "text": "  \"author\": {"
          },
          {
            "lineNumber": 6,
            "text": "    \"name\": \"Sybren A. Stüvel\""
          },
          {
            "lineNumber": 7,
            "text": "  },"
          },
          {
            "lineNumber": 8,
            "text": "  \"licenses\": ["
          },
          {
            "lineNumber": 9,
            "text": "    {"
          },
          {
            "lineNumber": 10,
            "text": "      \"type\": \"GPL-3.0\","
          },
          {
            "lineNumber": 11,
            "text": "      \"url\": \"https://opensource.org/licenses/GPL-3.0\""
          },
          {
            "lineNumber": 12,
            "text": "    }"
          },
          {
            "lineNumber": 13,
            "text": "  ],"
          },
          {
            "lineNumber": 14,
            "text": "  \"scripts\": {"
          },
          {
            "lineNumber": 15,
            "text": "    \"dev\": \"vite --port 8081 --base /app/ --mode development\","
          },
          {
            "lineNumber": 16,
            "text": "    \"build\": \"vite build\","
          },
          {
            "lineNumber": 17,
            "text": "    \"preview\": \"vite preview --port 5050\","
          },
          {
            "lineNumber": 18,
            "text": "    \"lint\": \"eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore\""
          },
          {
            "lineNumber": 19,
            "text": "  },"
          },
          {
            "lineNumber": 20,
            "text": "  \"dependencies\": {"
          },
          {
            "lineNumber": 21,
            "text": "    \"luxon\": \"^2.3.1\","
          },
          {
            "lineNumber": 22,
            "text": "    \"microtip\": \"^0.2.2\","
          },
          {
            "lineNumber": 23,
            "text": "    \"pinia\": \"^2.0.13\","
          },
          {
            "lineNumber": 24,
            "text": "    \"socket.io-client\": \"2\","
          },
          {
            "lineNumber": 25,
            "text": "    \"superagent\": \"^7.1.2\","
          },
          {
            "lineNumber": 26,
            "text": "    \"tabulator-tables\": \"^6.3.0\","
          },
          {
            "lineNumber": 27,
            "text": "    \"vue\": \"^3.2.31\","
          },
          {
            "lineNumber": 28,
            "text": "    \"vue-router\": \"^4.0.14\""
          },
          {
            "lineNumber": 29,
            "text": "  },"
          },
          {
            "lineNumber": 30,
            "text": "  \"devDependencies\": {"
          },
          {
            "lineNumber": 31,
            "text": "    \"@rushstack/eslint-patch\": \"^1.1.0\","
          },
          {
            "lineNumber": 32,
            "text": "    \"@vitejs/plugin-vue\": \"^2.3.1\","
          },
          {
            "lineNumber": 33,
            "text": "    \"@vue/eslint-config-prettier\": \"^7.0.0\","
          },
          {
            "lineNumber": 34,
            "text": "    \"eslint\": \"^8.5.0\","
          },
          {
            "lineNumber": 35,
            "text": "    \"eslint-plugin-vue\": \"^8.2.0\","
          },
          {
            "lineNumber": 36,
            "text": "    \"prettier\": \"^2.5.1\","
          },
          {
            "lineNumber": 37,
            "text": "    \"vite\": \"^2.9.1\""
          },
          {
            "lineNumber": 38,
            "text": "  }"
          },
          {
            "lineNumber": 39,
            "text": "}"
          }
        ],
        "range": {
          "endPosition": {
            "line": 39
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\web\\app\\package.json",
        "signatures": {}
      },
      "score": 0.36966830492019653
    },
    {
      "codeBlock": {
        "contents": "---\ntitle: Getting Started\nweight: 1\naliases:\n  - /devstart\n---\n\nTo start, get a **Git checkout** with either of these commands. The 1st one is for\npublic, read-only access. The 2nd one can be used if you have commit rights to\nthe project.\n\n&#96;&#96;&#96;\ngit clone https://projects.blender.org/studio/flamenco.git\ngit clone git@projects.blender.org:studio/flamenco.git\n&#96;&#96;&#96;\n\nThen follow the steps below to get everything up \u0026 running.\n\n## 1. Installing Go\n\nMost of Flamenco is made in Go.\n\n1. Install [the latest Go release](https://go.dev/). If you want to know specifically which version in required, check the\n   [go.mod](https://projects.blender.org/studio/flamenco/src/branch/main/go.mod) file.\n2. Optional: set the environment variable &#96;GOPATH&#96; to where you want Go to put its packages. Go will use &#96;$HOME/go&#96; by default.\n3. Ensure &#96;$GOPATH/bin&#96; is included in your &#96;$PATH&#96; environment variable. Run &#96;go env GOPATH&#96; if you're not sure what path to use.\n\n## 2. Installing NodeJS\n\nThe web UI is built with [Vue.js](https://vuejs.org/), and Socket.IO for\ncommunication with the backend. **NodeJS+Yarn** is used to collect all of those\nand build the frontend files.\n\n{{\u003c tabs \"installing-nodejs\" \u003e}}\n{{\u003c tab \"Linux\" \u003e}}\nIt's recommended to install Node via Snap:\n\n&#96;&#96;&#96;\nsudo snap install node --classic --channel=22\n&#96;&#96;&#96;\n\nIf you install NodeJS in a different way, it may not be bundled with Yarn. In that case, run:\n\n&#96;&#96;&#96;\nsudo npm install --global yarn\n&#96;&#96;&#96;\n\n{{\u003c /tab \u003e}}\n{{\u003c tab \"Windows\" \u003e}}\nInstall [Node v22 LTS](https://nodejs.org/en/download/). Be sure to enable the \"Automatically install the necessary tools\" checkbox.\n\nThen install Yarn via:\n\n&#96;&#96;&#96;\nnpm install --global yarn\n&#96;&#96;&#96;\n\n{{\u003c /tab \u003e}}\n{{\u003c tab \"macOS\" \u003e}}\n**Option 1** (Native install)\n\nInstall [Node v22 LTS](https://nodejs.org/en/download/) and then install Yarn via:\n\n&#96;&#96;&#96;\nnpm install --global yarn\n&#96;&#96;&#96;\n\n\u003cbr /\u003e\n\n**Option 2** (Homebrew)\n\nInstall Node 22 via homebrew:\n\n&#96;&#96;&#96;\nbrew install node@22\nbrew link node@22\n&#96;&#96;&#96;",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "---"
          },
          {
            "lineNumber": 2,
            "text": "title: Getting Started"
          },
          {
            "lineNumber": 3,
            "text": "weight: 1"
          },
          {
            "lineNumber": 4,
            "text": "aliases:"
          },
          {
            "lineNumber": 5,
            "text": "  - /devstart"
          },
          {
            "lineNumber": 6,
            "text": "---"
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "To start, get a **Git checkout** with either of these commands. The 1st one is for"
          },
          {
            "lineNumber": 9,
            "text": "public, read-only access. The 2nd one can be used if you have commit rights to"
          },
          {
            "lineNumber": 10,
            "text": "the project."
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 13,
            "text": "git clone https://projects.blender.org/studio/flamenco.git"
          },
          {
            "lineNumber": 14,
            "text": "git clone git@projects.blender.org:studio/flamenco.git"
          },
          {
            "lineNumber": 15,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "Then follow the steps below to get everything up \u0026 running."
          },
          {
            "lineNumber": 18
          },
          {
            "lineNumber": 19,
            "text": "## 1. Installing Go"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21,
            "text": "Most of Flamenco is made in Go."
          },
          {
            "lineNumber": 22
          },
          {
            "lineNumber": 23,
            "text": "1. Install [the latest Go release](https://go.dev/). If you want to know specifically which version in required, check the"
          },
          {
            "lineNumber": 24,
            "text": "   [go.mod](https://projects.blender.org/studio/flamenco/src/branch/main/go.mod) file."
          },
          {
            "lineNumber": 25,
            "text": "2. Optional: set the environment variable &#96;GOPATH&#96; to where you want Go to put its packages. Go will use &#96;$HOME/go&#96; by default."
          },
          {
            "lineNumber": 26,
            "text": "3. Ensure &#96;$GOPATH/bin&#96; is included in your &#96;$PATH&#96; environment variable. Run &#96;go env GOPATH&#96; if you're not sure what path to use."
          },
          {
            "lineNumber": 27
          },
          {
            "lineNumber": 28,
            "text": "## 2. Installing NodeJS"
          },
          {
            "lineNumber": 29
          },
          {
            "lineNumber": 30,
            "text": "The web UI is built with [Vue.js](https://vuejs.org/), and Socket.IO for"
          },
          {
            "lineNumber": 31,
            "text": "communication with the backend. **NodeJS+Yarn** is used to collect all of those"
          },
          {
            "lineNumber": 32,
            "text": "and build the frontend files."
          },
          {
            "lineNumber": 33
          },
          {
            "lineNumber": 34,
            "text": "{{\u003c tabs \"installing-nodejs\" \u003e}}"
          },
          {
            "lineNumber": 35,
            "text": "{{\u003c tab \"Linux\" \u003e}}"
          },
          {
            "lineNumber": 36,
            "text": "It's recommended to install Node via Snap:"
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 39,
            "text": "sudo snap install node --classic --channel=22"
          },
          {
            "lineNumber": 40,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 41
          },
          {
            "lineNumber": 42,
            "text": "If you install NodeJS in a different way, it may not be bundled with Yarn. In that case, run:"
          },
          {
            "lineNumber": 43
          },
          {
            "lineNumber": 44,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 45,
            "text": "sudo npm install --global yarn"
          },
          {
            "lineNumber": 46,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 47
          },
          {
            "lineNumber": 48,
            "text": "{{\u003c /tab \u003e}}"
          },
          {
            "lineNumber": 49,
            "text": "{{\u003c tab \"Windows\" \u003e}}"
          },
          {
            "lineNumber": 50,
            "text": "Install [Node v22 LTS](https://nodejs.org/en/download/). Be sure to enable the \"Automatically install the necessary tools\" checkbox."
          },
          {
            "lineNumber": 51
          },
          {
            "lineNumber": 52,
            "text": "Then install Yarn via:"
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 55,
            "text": "npm install --global yarn"
          },
          {
            "lineNumber": 56,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 57
          },
          {
            "lineNumber": 58,
            "text": "{{\u003c /tab \u003e}}"
          },
          {
            "lineNumber": 59,
            "text": "{{\u003c tab \"macOS\" \u003e}}"
          },
          {
            "lineNumber": 60,
            "text": "**Option 1** (Native install)"
          },
          {
            "lineNumber": 61
          },
          {
            "lineNumber": 62,
            "text": "Install [Node v22 LTS](https://nodejs.org/en/download/) and then install Yarn via:"
          },
          {
            "lineNumber": 63
          },
          {
            "lineNumber": 64,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 65,
            "text": "npm install --global yarn"
          },
          {
            "lineNumber": 66,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 67
          },
          {
            "lineNumber": 68,
            "text": "\u003cbr /\u003e"
          },
          {
            "lineNumber": 69
          },
          {
            "lineNumber": 70,
            "text": "**Option 2** (Homebrew)"
          },
          {
            "lineNumber": 71
          },
          {
            "lineNumber": 72,
            "text": "Install Node 22 via homebrew:"
          },
          {
            "lineNumber": 73
          },
          {
            "lineNumber": 74,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 75,
            "text": "brew install node@22"
          },
          {
            "lineNumber": 76,
            "text": "brew link node@22"
          },
          {
            "lineNumber": 77,
            "text": "&#96;&#96;&#96;"
          }
        ],
        "range": {
          "endPosition": {
            "line": 78
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\getting-started\\_index.md",
        "signatures": {}
      },
      "score": 0.36476683616638184
    },
    {
      "codeBlock": {
        "contents": "*.exe\n*.sqlite\n*.sqlite-shm\n*.sqlite-wal\n*.db\n*.blend[0-9]\n*.flamenco.blend\n.env\n\n/flamenco-manager\n/flamenco-worker\n/flamenco-manager_race\n/flamenco-worker_race\n/shaman-checkout-id-setter\n/stresser\n/stresser.csv\n/job-creator\n/mage\n/addon-packer\nflamenco-manager.yaml\nflamenco-worker.yaml\nflamenco-worker-credentials.yaml\nnode_modules/\n/vendor/\n\n# Mockgen creates those files temporarily, and they can linger when it\n# fails to run properly. They should never be committed to Git.\ngomock_reflect_*\n\n# Directory for custom job compiler scripts:\n/scripts/\n\n# Default storage locations:\n/flamenco-manager-storage/\n/flamenco-shared-storage/\n\n# Old default storage locations:\n/task-logs/\n/flamenco-storage/\n\n__pycache__\n*.pyc\n.mypy_cache/\n.openapi-generator/\n.hugo_build.lock\n.openapi-generator-*.log\n\nweb/manager-api/dist/\nweb/static/\nweb/project-website/public/\nweb/project-website/resources/_gen/\n/dist/\n/tools/\n\n# IDE related stuff\n*.DS_Store\n.vscode/settings.json\n.vscode/launch.json",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "*.exe"
          },
          {
            "lineNumber": 2,
            "text": "*.sqlite"
          },
          {
            "lineNumber": 3,
            "text": "*.sqlite-shm"
          },
          {
            "lineNumber": 4,
            "text": "*.sqlite-wal"
          },
          {
            "lineNumber": 5,
            "text": "*.db"
          },
          {
            "lineNumber": 6,
            "text": "*.blend[0-9]"
          },
          {
            "lineNumber": 7,
            "text": "*.flamenco.blend"
          },
          {
            "lineNumber": 8,
            "text": ".env"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "/flamenco-manager"
          },
          {
            "lineNumber": 11,
            "text": "/flamenco-worker"
          },
          {
            "lineNumber": 12,
            "text": "/flamenco-manager_race"
          },
          {
            "lineNumber": 13,
            "text": "/flamenco-worker_race"
          },
          {
            "lineNumber": 14,
            "text": "/shaman-checkout-id-setter"
          },
          {
            "lineNumber": 15,
            "text": "/stresser"
          },
          {
            "lineNumber": 16,
            "text": "/stresser.csv"
          },
          {
            "lineNumber": 17,
            "text": "/job-creator"
          },
          {
            "lineNumber": 18,
            "text": "/mage"
          },
          {
            "lineNumber": 19,
            "text": "/addon-packer"
          },
          {
            "lineNumber": 20,
            "text": "flamenco-manager.yaml"
          },
          {
            "lineNumber": 21,
            "text": "flamenco-worker.yaml"
          },
          {
            "lineNumber": 22,
            "text": "flamenco-worker-credentials.yaml"
          },
          {
            "lineNumber": 23,
            "text": "node_modules/"
          },
          {
            "lineNumber": 24,
            "text": "/vendor/"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "# Mockgen creates those files temporarily, and they can linger when it"
          },
          {
            "lineNumber": 27,
            "text": "# fails to run properly. They should never be committed to Git."
          },
          {
            "lineNumber": 28,
            "text": "gomock_reflect_*"
          },
          {
            "lineNumber": 29
          },
          {
            "lineNumber": 30,
            "text": "# Directory for custom job compiler scripts:"
          },
          {
            "lineNumber": 31,
            "text": "/scripts/"
          },
          {
            "lineNumber": 32
          },
          {
            "lineNumber": 33,
            "text": "# Default storage locations:"
          },
          {
            "lineNumber": 34,
            "text": "/flamenco-manager-storage/"
          },
          {
            "lineNumber": 35,
            "text": "/flamenco-shared-storage/"
          },
          {
            "lineNumber": 36
          },
          {
            "lineNumber": 37,
            "text": "# Old default storage locations:"
          },
          {
            "lineNumber": 38,
            "text": "/task-logs/"
          },
          {
            "lineNumber": 39,
            "text": "/flamenco-storage/"
          },
          {
            "lineNumber": 40
          },
          {
            "lineNumber": 41,
            "text": "__pycache__"
          },
          {
            "lineNumber": 42,
            "text": "*.pyc"
          },
          {
            "lineNumber": 43,
            "text": ".mypy_cache/"
          },
          {
            "lineNumber": 44,
            "text": ".openapi-generator/"
          },
          {
            "lineNumber": 45,
            "text": ".hugo_build.lock"
          },
          {
            "lineNumber": 46,
            "text": ".openapi-generator-*.log"
          },
          {
            "lineNumber": 47
          },
          {
            "lineNumber": 48,
            "text": "web/manager-api/dist/"
          },
          {
            "lineNumber": 49,
            "text": "web/static/"
          },
          {
            "lineNumber": 50,
            "text": "web/project-website/public/"
          },
          {
            "lineNumber": 51,
            "text": "web/project-website/resources/_gen/"
          },
          {
            "lineNumber": 52,
            "text": "/dist/"
          },
          {
            "lineNumber": 53,
            "text": "/tools/"
          },
          {
            "lineNumber": 54
          },
          {
            "lineNumber": 55,
            "text": "# IDE related stuff"
          },
          {
            "lineNumber": 56,
            "text": "*.DS_Store"
          },
          {
            "lineNumber": 57,
            "text": ".vscode/settings.json"
          },
          {
            "lineNumber": 58,
            "text": ".vscode/launch.json"
          }
        ],
        "range": {
          "endPosition": {
            "line": 58
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\.gitignore",
        "signatures": {}
      },
      "score": 0.3489690124988556
    },
    {
      "codeBlock": {
        "contents": "# Flamenco Blender add-on\n\n## Setting up development environment\n\n&#96;&#96;&#96;\n~/workspace/blender-git/build_linux/bin/3.1/python/bin/python3.9 -m venv --upgrade-deps venv\n. ./venv/bin/activate\npip install poetry\npoetry install\n&#96;&#96;&#96;\n\n## Generating the OpenAPI client\n\n1. Make sure Java is installed (so &#96;java --version&#96; shows something sensible).\n2. In the root directory of the repository, run &#96;make generate-py&#96;\n\n\n## Type annotations and lazy imports\n\nThis add-on tries to only load Python packages from wheel files when necessary. Loading things from wheels is tricky, as they basically pollute the &#96;sys.modules&#96; dictionary and thus can \"leak\" to other add-ons. This can cause conflicts when, for example, another add-on is using a different version of the same package.\n\nThe result is that sometimes there are some strange hoops to jump through. The most obvious one is for type annotations. This is why you'll see code like:\n\n&#96;&#96;&#96;\nif TYPE_CHECKING:\n    from .bat_interface import _PackThread\nelse:\n    _PackThread = object\n&#96;&#96;&#96;\n\nThis makes it possible to declare a function with &#96;def func() -\u003e _PackThread&#96;, without having to load &#96;bat_interface&#96; immediately at import time.",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# Flamenco Blender add-on"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "## Setting up development environment"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 6,
            "text": "~/workspace/blender-git/build_linux/bin/3.1/python/bin/python3.9 -m venv --upgrade-deps venv"
          },
          {
            "lineNumber": 7,
            "text": ". ./venv/bin/activate"
          },
          {
            "lineNumber": 8,
            "text": "pip install poetry"
          },
          {
            "lineNumber": 9,
            "text": "poetry install"
          },
          {
            "lineNumber": 10,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "## Generating the OpenAPI client"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "1. Make sure Java is installed (so &#96;java --version&#96; shows something sensible)."
          },
          {
            "lineNumber": 15,
            "text": "2. In the root directory of the repository, run &#96;make generate-py&#96;"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "## Type annotations and lazy imports"
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20,
            "text": "This add-on tries to only load Python packages from wheel files when necessary. Loading things from wheels is tricky, as they basically pollute the &#96;sys.modules&#96; dictionary and thus can \"leak\" to other add-ons. This can cause conflicts when, for example, another add-on is using a different version of the same package."
          },
          {
            "lineNumber": 21
          },
          {
            "lineNumber": 22,
            "text": "The result is that sometimes there are some strange hoops to jump through. The most obvious one is for type annotations. This is why you'll see code like:"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 25,
            "text": "if TYPE_CHECKING:"
          },
          {
            "lineNumber": 26,
            "text": "    from .bat_interface import _PackThread"
          },
          {
            "lineNumber": 27,
            "text": "else:"
          },
          {
            "lineNumber": 28,
            "text": "    _PackThread = object"
          },
          {
            "lineNumber": 29,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "This makes it possible to declare a function with &#96;def func() -\u003e _PackThread&#96;, without having to load &#96;bat_interface&#96; immediately at import time."
          }
        ],
        "range": {
          "endPosition": {
            "line": 31
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\README.md",
        "signatures": {}
      },
      "score": 0.3200674057006836
    },
    {
      "codeBlock": {
        "contents": "{\n  \"recommendations\": [\n    \"golang.go\",\n    \"alexcvzz.vscode-sqlite\",\n    \"netcorext.uuid-generator\",\n    \"esbenp.prettier-vscode\",\n    \"Vue.volar\"\n  ]\n}",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "{"
          },
          {
            "lineNumber": 2,
            "text": "  \"recommendations\": ["
          },
          {
            "lineNumber": 3,
            "text": "    \"golang.go\","
          },
          {
            "lineNumber": 4,
            "text": "    \"alexcvzz.vscode-sqlite\","
          },
          {
            "lineNumber": 5,
            "text": "    \"netcorext.uuid-generator\","
          },
          {
            "lineNumber": 6,
            "text": "    \"esbenp.prettier-vscode\","
          },
          {
            "lineNumber": 7,
            "text": "    \"Vue.volar\""
          },
          {
            "lineNumber": 8,
            "text": "  ]"
          },
          {
            "lineNumber": 9,
            "text": "}"
          }
        ],
        "range": {
          "endPosition": {
            "line": 9
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\.vscode\\extensions.json",
        "signatures": {}
      },
      "score": 0.31250250339508057
    },
    {
      "codeBlock": {
        "contents": "---\ntitle: Generating Code\nweight: 20\n---\n\nSome code (Go, Python, JavaScript) is generated from the OpenAPI specs in\n&#96;pkg/api/flamenco-openapi.yaml&#96;. There are also Go files generated to create\nmock implementations of interfaces for unit testing purposes.\n\n## Installing the Code Generators\n\nThere are three code generators used by Flamenco:\n\n- [&#96;oapi-codegen&#96;][oapi-codegen] for the OpenAPI server \u0026 client in Go.\n- [&#96;mockgen&#96;][mockgen] for generating mocks for tests in Go.\n- [&#96;openapi-codegen&#96;][openapi-codegen] for the OpenAPI clients in Python and JavaScript.\n\n[oapi-codegen]: https://github.com/deepmap/oapi-codegen/cmd/oapi-codegen\n[mockgen]: https://github.com/golang/mock/mockgen\n[openapi-codegen]: https://openapi-generator.tech/\n\n### Go code generators\n\nThe first two generators can be installed with either of these commands:\n\n&#96;&#96;&#96;bash\n# Simplest way to install the Go generators:\n$ go run mage.go installGenerators\n\n# Faster to re-run than the above, but does require Make:\n$ make install-generators\n&#96;&#96;&#96;\n\n### Python and JavaScript code generators\n\n\n&#96;openapi-codegen&#96; is bundled with the Flamenco sources, but does need a Java\nruntime environment to be installed.\n\n{{\u003c tabs \"installing-java\" \u003e}}\n{{\u003c tab \"Linux\" \u003e}}\n\nOn Ubuntu Linux this should be enough:\n\n&#96;&#96;&#96;bash\n$ sudo apt install default-jre-headless\n&#96;&#96;&#96;\n\nOther Linux distributions very likely have a similar package.\n\n{{\u003c /tab \u003e}}\n{{\u003c tab \"Windows\" \u003e}}\n\nUse the [official Java installer](https://www.java.com/en/download/manual.jsp).\n\n{{\u003c /tab \u003e}}\n{{\u003c tab \"macOS\" \u003e}}\n\n**Option 1** (Native install)\n\nUse the [official Java installer](https://www.java.com/en/download/manual.jsp).\n\n\u003cbr /\u003e\n\n**Option 2** (Homebrew)\n\nInstall Java via homebrew:\n\n&#96;&#96;&#96;\nbrew install java\n&#96;&#96;&#96;\n\nNote that this requires XCode to be installed.\n\n{{\u003c /tab \u003e}}\n{{\u003c /tabs \u003e}}\n\n\n\n## Committing Generated Code\n\n**Generated code is committed to Git**, so that after a checkout you shouldn't\nneed to re-run the generator to build Flamenco.",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "---"
          },
          {
            "lineNumber": 2,
            "text": "title: Generating Code"
          },
          {
            "lineNumber": 3,
            "text": "weight: 20"
          },
          {
            "lineNumber": 4,
            "text": "---"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "Some code (Go, Python, JavaScript) is generated from the OpenAPI specs in"
          },
          {
            "lineNumber": 7,
            "text": "&#96;pkg/api/flamenco-openapi.yaml&#96;. There are also Go files generated to create"
          },
          {
            "lineNumber": 8,
            "text": "mock implementations of interfaces for unit testing purposes."
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "## Installing the Code Generators"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "There are three code generators used by Flamenco:"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "- [&#96;oapi-codegen&#96;][oapi-codegen] for the OpenAPI server \u0026 client in Go."
          },
          {
            "lineNumber": 15,
            "text": "- [&#96;mockgen&#96;][mockgen] for generating mocks for tests in Go."
          },
          {
            "lineNumber": 16,
            "text": "- [&#96;openapi-codegen&#96;][openapi-codegen] for the OpenAPI clients in Python and JavaScript."
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "[oapi-codegen]: https://github.com/deepmap/oapi-codegen/cmd/oapi-codegen"
          },
          {
            "lineNumber": 19,
            "text": "[mockgen]: https://github.com/golang/mock/mockgen"
          },
          {
            "lineNumber": 20,
            "text": "[openapi-codegen]: https://openapi-generator.tech/"
          },
          {
            "lineNumber": 21
          },
          {
            "lineNumber": 22,
            "text": "### Go code generators"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "The first two generators can be installed with either of these commands:"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "&#96;&#96;&#96;bash"
          },
          {
            "lineNumber": 27,
            "text": "# Simplest way to install the Go generators:"
          },
          {
            "lineNumber": 28,
            "text": "$ go run mage.go installGenerators"
          },
          {
            "lineNumber": 29
          },
          {
            "lineNumber": 30,
            "text": "# Faster to re-run than the above, but does require Make:"
          },
          {
            "lineNumber": 31,
            "text": "$ make install-generators"
          },
          {
            "lineNumber": 32,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 33
          },
          {
            "lineNumber": 34,
            "text": "### Python and JavaScript code generators"
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36
          },
          {
            "lineNumber": 37,
            "text": "&#96;openapi-codegen&#96; is bundled with the Flamenco sources, but does need a Java"
          },
          {
            "lineNumber": 38,
            "text": "runtime environment to be installed."
          },
          {
            "lineNumber": 39
          },
          {
            "lineNumber": 40,
            "text": "{{\u003c tabs \"installing-java\" \u003e}}"
          },
          {
            "lineNumber": 41,
            "text": "{{\u003c tab \"Linux\" \u003e}}"
          },
          {
            "lineNumber": 42
          },
          {
            "lineNumber": 43,
            "text": "On Ubuntu Linux this should be enough:"
          },
          {
            "lineNumber": 44
          },
          {
            "lineNumber": 45,
            "text": "&#96;&#96;&#96;bash"
          },
          {
            "lineNumber": 46,
            "text": "$ sudo apt install default-jre-headless"
          },
          {
            "lineNumber": 47,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 48
          },
          {
            "lineNumber": 49,
            "text": "Other Linux distributions very likely have a similar package."
          },
          {
            "lineNumber": 50
          },
          {
            "lineNumber": 51,
            "text": "{{\u003c /tab \u003e}}"
          },
          {
            "lineNumber": 52,
            "text": "{{\u003c tab \"Windows\" \u003e}}"
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54,
            "text": "Use the [official Java installer](https://www.java.com/en/download/manual.jsp)."
          },
          {
            "lineNumber": 55
          },
          {
            "lineNumber": 56,
            "text": "{{\u003c /tab \u003e}}"
          },
          {
            "lineNumber": 57,
            "text": "{{\u003c tab \"macOS\" \u003e}}"
          },
          {
            "lineNumber": 58
          },
          {
            "lineNumber": 59,
            "text": "**Option 1** (Native install)"
          },
          {
            "lineNumber": 60
          },
          {
            "lineNumber": 61,
            "text": "Use the [official Java installer](https://www.java.com/en/download/manual.jsp)."
          },
          {
            "lineNumber": 62
          },
          {
            "lineNumber": 63,
            "text": "\u003cbr /\u003e"
          },
          {
            "lineNumber": 64
          },
          {
            "lineNumber": 65,
            "text": "**Option 2** (Homebrew)"
          },
          {
            "lineNumber": 66
          },
          {
            "lineNumber": 67,
            "text": "Install Java via homebrew:"
          },
          {
            "lineNumber": 68
          },
          {
            "lineNumber": 69,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 70,
            "text": "brew install java"
          },
          {
            "lineNumber": 71,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 72
          },
          {
            "lineNumber": 73,
            "text": "Note that this requires XCode to be installed."
          },
          {
            "lineNumber": 74
          },
          {
            "lineNumber": 75,
            "text": "{{\u003c /tab \u003e}}"
          },
          {
            "lineNumber": 76,
            "text": "{{\u003c /tabs \u003e}}"
          },
          {
            "lineNumber": 77
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79
          },
          {
            "lineNumber": 80,
            "text": "## Committing Generated Code"
          },
          {
            "lineNumber": 81
          },
          {
            "lineNumber": 82,
            "text": "**Generated code is committed to Git**, so that after a checkout you shouldn't"
          },
          {
            "lineNumber": 83,
            "text": "need to re-run the generator to build Flamenco."
          }
        ],
        "range": {
          "endPosition": {
            "line": 84
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\generating-code\\_index.md",
        "signatures": {}
      },
      "score": 0.31085529923439026
    },
    {
      "codeBlock": {
        "contents": "---\ntitle: Database\nweight: 50\n---\n\nFlamenco Manager and Worker use SQLite as database, sqlc as object-relational\nmapper, and Goose for schema migrations.\n\n{{\u003c hint type=important \u003e}}\nSome of these tools assume that you have your database in\n&#96;flamenco-manager.sqlite&#96;. Even though Flamenco Manager can store its database\nanywhere, these development tools are not as flexible.\n\nSo for simplicity sake, set &#96;database: flamenco-manager.sqlite&#96; in your\n&#96;flamenco-manager.yaml&#96;.\n{{\u003c /hint \u003e}}\n\n## SQLC\n\nFlamenco uses [sqlc](https://sqlc.dev/) for interfacing with its SQLite database.\n\n### Installing SQLC\n\nSQLC can be used via &#96;go tool sqlc&#96;. This will run the tool, downloading \u0026\nbuilding it if necessary. This does depend on a C/C++ compiler, so if you do not\nhave one, or get build errors, the [precompiled sqlc binaries][sqlc-precompiled]\nwork just as well. Choose whatever works for you.\n\nBecause of the above, SQLC is not part of &#96;make with-deps&#96; and not included in\n&#96;make generate-go&#96;.\n\n### Using SQLC\n\nThe Manager and Worker SQL files can be found in\n&#96;internal/manager/persistence/sqlc&#96; and &#96;internal/worker/persistence/sqlc&#96;.\nAfter updating the &#96;.sql&#96; files, run:\n\n&#96;&#96;&#96;shell\n\u003e go tool sqlc generate   # Easiest to get running, if it works for you.\n\u003e sqlc generate           # If you got the precompiled binaries.\n&#96;&#96;&#96;\n\nFor handling schema changes and database versioning, see below.\n\n{{\u003c hint type=note \u003e}}\nRunning sqlc itself is only necessary to regenerate the database code. Once\ngenerated, the code is independent of sqlc.\n{{\u003c /hint \u003e}}\n\n[sqlc-precompiled]: https://docs.sqlc.dev/en/latest/overview/install.html#downloads\n\n### Handling Schema changes\n\nDatabase schema changes are managed with [Goose][goose]. Every change is defined\nin a separate SQL file, and has the queries to make the change and to roll it\nback. Of course not all changes can be losslessly rolled back.\n\nSQLC needs to know the final schema those Goose migrations produced. After\nadding a migration, you can use this little helper tool to regenerate the SQLC",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "---"
          },
          {
            "lineNumber": 2,
            "text": "title: Database"
          },
          {
            "lineNumber": 3,
            "text": "weight: 50"
          },
          {
            "lineNumber": 4,
            "text": "---"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "Flamenco Manager and Worker use SQLite as database, sqlc as object-relational"
          },
          {
            "lineNumber": 7,
            "text": "mapper, and Goose for schema migrations."
          },
          {
            "lineNumber": 8
          },
          {
            "lineNumber": 9,
            "text": "{{\u003c hint type=important \u003e}}"
          },
          {
            "lineNumber": 10,
            "text": "Some of these tools assume that you have your database in"
          },
          {
            "lineNumber": 11,
            "text": "&#96;flamenco-manager.sqlite&#96;. Even though Flamenco Manager can store its database"
          },
          {
            "lineNumber": 12,
            "text": "anywhere, these development tools are not as flexible."
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "So for simplicity sake, set &#96;database: flamenco-manager.sqlite&#96; in your"
          },
          {
            "lineNumber": 15,
            "text": "&#96;flamenco-manager.yaml&#96;."
          },
          {
            "lineNumber": 16,
            "text": "{{\u003c /hint \u003e}}"
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "## SQLC"
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20,
            "text": "Flamenco uses [sqlc](https://sqlc.dev/) for interfacing with its SQLite database."
          },
          {
            "lineNumber": 21
          },
          {
            "lineNumber": 22,
            "text": "### Installing SQLC"
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "SQLC can be used via &#96;go tool sqlc&#96;. This will run the tool, downloading \u0026"
          },
          {
            "lineNumber": 25,
            "text": "building it if necessary. This does depend on a C/C++ compiler, so if you do not"
          },
          {
            "lineNumber": 26,
            "text": "have one, or get build errors, the [precompiled sqlc binaries][sqlc-precompiled]"
          },
          {
            "lineNumber": 27,
            "text": "work just as well. Choose whatever works for you."
          },
          {
            "lineNumber": 28
          },
          {
            "lineNumber": 29,
            "text": "Because of the above, SQLC is not part of &#96;make with-deps&#96; and not included in"
          },
          {
            "lineNumber": 30,
            "text": "&#96;make generate-go&#96;."
          },
          {
            "lineNumber": 31
          },
          {
            "lineNumber": 32,
            "text": "### Using SQLC"
          },
          {
            "lineNumber": 33
          },
          {
            "lineNumber": 34,
            "text": "The Manager and Worker SQL files can be found in"
          },
          {
            "lineNumber": 35,
            "text": "&#96;internal/manager/persistence/sqlc&#96; and &#96;internal/worker/persistence/sqlc&#96;."
          },
          {
            "lineNumber": 36,
            "text": "After updating the &#96;.sql&#96; files, run:"
          },
          {
            "lineNumber": 37
          },
          {
            "lineNumber": 38,
            "text": "&#96;&#96;&#96;shell"
          },
          {
            "lineNumber": 39,
            "text": "\u003e go tool sqlc generate   # Easiest to get running, if it works for you."
          },
          {
            "lineNumber": 40,
            "text": "\u003e sqlc generate           # If you got the precompiled binaries."
          },
          {
            "lineNumber": 41,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 42
          },
          {
            "lineNumber": 43,
            "text": "For handling schema changes and database versioning, see below."
          },
          {
            "lineNumber": 44
          },
          {
            "lineNumber": 45,
            "text": "{{\u003c hint type=note \u003e}}"
          },
          {
            "lineNumber": 46,
            "text": "Running sqlc itself is only necessary to regenerate the database code. Once"
          },
          {
            "lineNumber": 47,
            "text": "generated, the code is independent of sqlc."
          },
          {
            "lineNumber": 48,
            "text": "{{\u003c /hint \u003e}}"
          },
          {
            "lineNumber": 49
          },
          {
            "lineNumber": 50,
            "text": "[sqlc-precompiled]: https://docs.sqlc.dev/en/latest/overview/install.html#downloads"
          },
          {
            "lineNumber": 51
          },
          {
            "lineNumber": 52,
            "text": "### Handling Schema changes"
          },
          {
            "lineNumber": 53
          },
          {
            "lineNumber": 54,
            "text": "Database schema changes are managed with [Goose][goose]. Every change is defined"
          },
          {
            "lineNumber": 55,
            "text": "in a separate SQL file, and has the queries to make the change and to roll it"
          },
          {
            "lineNumber": 56,
            "text": "back. Of course not all changes can be losslessly rolled back."
          },
          {
            "lineNumber": 57
          },
          {
            "lineNumber": 58,
            "text": "SQLC needs to know the final schema those Goose migrations produced. After"
          },
          {
            "lineNumber": 59,
            "text": "adding a migration, you can use this little helper tool to regenerate the SQLC"
          }
        ],
        "range": {
          "endPosition": {
            "line": 59
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\database\\_index.md",
        "signatures": {}
      },
      "score": 0.30672380328178406
    },
    {
      "codeBlock": {
        "contents": "# EditorConfig is awesome: https://EditorConfig.org\nroot=true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 2\nmax_line_length = 100\n\n[*.go]\nindent_style = tab\n\n[*.py]\nindent_size = 4\n\n[Makefile]\nindent_style = tab",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# EditorConfig is awesome: https://EditorConfig.org"
          },
          {
            "lineNumber": 2,
            "text": "root=true"
          },
          {
            "lineNumber": 3
          },
          {
            "lineNumber": 4,
            "text": "[*]"
          },
          {
            "lineNumber": 5,
            "text": "charset = utf-8"
          },
          {
            "lineNumber": 6,
            "text": "end_of_line = lf"
          },
          {
            "lineNumber": 7,
            "text": "insert_final_newline = true"
          },
          {
            "lineNumber": 8,
            "text": "trim_trailing_whitespace = true"
          },
          {
            "lineNumber": 9,
            "text": "indent_style = space"
          },
          {
            "lineNumber": 10,
            "text": "indent_size = 2"
          },
          {
            "lineNumber": 11,
            "text": "max_line_length = 100"
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13,
            "text": "[*.go]"
          },
          {
            "lineNumber": 14,
            "text": "indent_style = tab"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "[*.py]"
          },
          {
            "lineNumber": 17,
            "text": "indent_size = 4"
          },
          {
            "lineNumber": 18
          },
          {
            "lineNumber": 19,
            "text": "[Makefile]"
          },
          {
            "lineNumber": 20,
            "text": "indent_style = tab"
          }
        ],
        "range": {
          "endPosition": {
            "line": 20
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\.editorconfig",
        "signatures": {}
      },
      "score": 0.3059925436973572
    },
    {
      "codeBlock": {
        "contents": "module projects.blender.org/studio/flamenco\n\ngo 1.25.5\n\nrequire (\n\tgithub.com/adrg/xdg v0.4.0\n\tgithub.com/alessio/shellescape v1.4.2\n\tgithub.com/benbjohnson/clock v1.3.0\n\tgithub.com/deepmap/oapi-codegen v1.9.0\n\tgithub.com/disintegration/imaging v1.6.2\n\tgithub.com/dop251/goja v0.0.0-20230812105242-81d76064690d\n\tgithub.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d\n\tgithub.com/eclipse/paho.golang v0.22.0\n\tgithub.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e\n\tgithub.com/gertd/go-pluralize v0.2.1\n\tgithub.com/getkin/kin-openapi v0.132.0\n\tgithub.com/golang/mock v1.6.0\n\tgithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510\n\tgithub.com/google/uuid v1.6.0\n\tgithub.com/graarh/golang-socketio v0.0.0-20170510162725-2c44953b9b5f\n\tgithub.com/labstack/echo/v4 v4.9.1\n\tgithub.com/magefile/mage v1.15.0\n\tgithub.com/mattn/go-colorable v0.1.13\n\tgithub.com/mochi-mqtt/server/v2 v2.7.9\n\tgithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c\n\tgithub.com/powerman/fileuri v0.2.0\n\tgithub.com/pressly/goose/v3 v3.25.0\n\tgithub.com/rs/zerolog v1.33.0\n\tgithub.com/samber/slog-zerolog/v2 v2.7.3\n\tgithub.com/stretchr/testify v1.11.0\n\tgithub.com/zcalusic/sysinfo v1.0.1\n\tgithub.com/ziflex/lecho/v3 v3.1.0\n\tgolang.org/x/crypto v0.46.0\n\tgolang.org/x/image v0.18.0\n\tgolang.org/x/net v0.48.0\n\tgolang.org/x/sync v0.19.0\n\tgolang.org/x/sys v0.39.0\n\tgolang.org/x/vuln v1.1.3\n\tgopkg.in/yaml.v2 v2.4.0\n\thonnef.co/go/tools v0.5.1\n\tmodernc.org/sqlite v1.40.1\n)\n\nrequire (\n\tcel.dev/expr v0.19.1 // indirect",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "module projects.blender.org/studio/flamenco"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "go 1.25.5"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "require ("
          },
          {
            "lineNumber": 6,
            "text": "\tgithub.com/adrg/xdg v0.4.0"
          },
          {
            "lineNumber": 7,
            "text": "\tgithub.com/alessio/shellescape v1.4.2"
          },
          {
            "lineNumber": 8,
            "text": "\tgithub.com/benbjohnson/clock v1.3.0"
          },
          {
            "lineNumber": 9,
            "text": "\tgithub.com/deepmap/oapi-codegen v1.9.0"
          },
          {
            "lineNumber": 10,
            "text": "\tgithub.com/disintegration/imaging v1.6.2"
          },
          {
            "lineNumber": 11,
            "text": "\tgithub.com/dop251/goja v0.0.0-20230812105242-81d76064690d"
          },
          {
            "lineNumber": 12,
            "text": "\tgithub.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d"
          },
          {
            "lineNumber": 13,
            "text": "\tgithub.com/eclipse/paho.golang v0.22.0"
          },
          {
            "lineNumber": 14,
            "text": "\tgithub.com/fromkeith/gossdp v0.0.0-20180102154144-1b2c43f6886e"
          },
          {
            "lineNumber": 15,
            "text": "\tgithub.com/gertd/go-pluralize v0.2.1"
          },
          {
            "lineNumber": 16,
            "text": "\tgithub.com/getkin/kin-openapi v0.132.0"
          },
          {
            "lineNumber": 17,
            "text": "\tgithub.com/golang/mock v1.6.0"
          },
          {
            "lineNumber": 18,
            "text": "\tgithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510"
          },
          {
            "lineNumber": 19,
            "text": "\tgithub.com/google/uuid v1.6.0"
          },
          {
            "lineNumber": 20,
            "text": "\tgithub.com/graarh/golang-socketio v0.0.0-20170510162725-2c44953b9b5f"
          },
          {
            "lineNumber": 21,
            "text": "\tgithub.com/labstack/echo/v4 v4.9.1"
          },
          {
            "lineNumber": 22,
            "text": "\tgithub.com/magefile/mage v1.15.0"
          },
          {
            "lineNumber": 23,
            "text": "\tgithub.com/mattn/go-colorable v0.1.13"
          },
          {
            "lineNumber": 24,
            "text": "\tgithub.com/mochi-mqtt/server/v2 v2.7.9"
          },
          {
            "lineNumber": 25,
            "text": "\tgithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c"
          },
          {
            "lineNumber": 26,
            "text": "\tgithub.com/powerman/fileuri v0.2.0"
          },
          {
            "lineNumber": 27,
            "text": "\tgithub.com/pressly/goose/v3 v3.25.0"
          },
          {
            "lineNumber": 28,
            "text": "\tgithub.com/rs/zerolog v1.33.0"
          },
          {
            "lineNumber": 29,
            "text": "\tgithub.com/samber/slog-zerolog/v2 v2.7.3"
          },
          {
            "lineNumber": 30,
            "text": "\tgithub.com/stretchr/testify v1.11.0"
          },
          {
            "lineNumber": 31,
            "text": "\tgithub.com/zcalusic/sysinfo v1.0.1"
          },
          {
            "lineNumber": 32,
            "text": "\tgithub.com/ziflex/lecho/v3 v3.1.0"
          },
          {
            "lineNumber": 33,
            "text": "\tgolang.org/x/crypto v0.46.0"
          },
          {
            "lineNumber": 34,
            "text": "\tgolang.org/x/image v0.18.0"
          },
          {
            "lineNumber": 35,
            "text": "\tgolang.org/x/net v0.48.0"
          },
          {
            "lineNumber": 36,
            "text": "\tgolang.org/x/sync v0.19.0"
          },
          {
            "lineNumber": 37,
            "text": "\tgolang.org/x/sys v0.39.0"
          },
          {
            "lineNumber": 38,
            "text": "\tgolang.org/x/vuln v1.1.3"
          },
          {
            "lineNumber": 39,
            "text": "\tgopkg.in/yaml.v2 v2.4.0"
          },
          {
            "lineNumber": 40,
            "text": "\thonnef.co/go/tools v0.5.1"
          },
          {
            "lineNumber": 41,
            "text": "\tmodernc.org/sqlite v1.40.1"
          },
          {
            "lineNumber": 42,
            "text": ")"
          },
          {
            "lineNumber": 43
          },
          {
            "lineNumber": 44,
            "text": "require ("
          },
          {
            "lineNumber": 45,
            "text": "\tcel.dev/expr v0.19.1 // indirect"
          }
        ],
        "range": {
          "endPosition": {
            "line": 45
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\go.mod",
        "signatures": {}
      },
      "score": 0.2935546040534973
    },
    {
      "codeBlock": {
        "contents": "---\ntitle: Building Flamenco\nweight: 10\n---\n\nFor the steps towards your first build, see [Getting Started][start].\n\n[start]: {{\u003c relref \"../getting-started \"\u003e}}\n\n## Building with Magefile\n\nThe Flamenco build tool is made in Go, using [Magefile][mage].\n\n[mage]: https://magefile.org/\n\n### Basic Builds\n\n&#96;&#96;&#96;sh\n$ go run mage.go build\n&#96;&#96;&#96;\n\nThis builds Flamenco Manager, including its webapp, and Flamenco Worker.\n\n### Listing Build Targets\n\n&#96;&#96;&#96;\n$ go run mage.go -l\n&#96;&#96;&#96;\n\nWill list these targets:\n\n| Target                       | Description                                                                                          |\n|------------------------------|------------------------------------------------------------------------------------------------------|\n| build                        | Flamenco Manager and Flamenco Worker, including the webapp and the add-on                            |\n| check                        | Run unit tests, check for vulnerabilities, and run the linter                                        |\n| clean                        | Remove executables and other build output                                                            |\n| devServerWebapp              |                                                                                                      |\n| flamencoManager              | Build Flamenco Manager with the webapp and add-on ZIP embedded                                       |\n| flamencoManagerRace          | Build the Flamenco Manager executable with race condition checker enabled, do not rebuild the webapp |\n| flamencoManagerWithoutWebapp | Only build the Flamenco Manager executable, do not rebuild the webapp                                |\n| flamencoWorker               | Build the Flamenco Worker executable                                                                 |\n| format                       | Run &#96;gofmt&#96;, formatting all the source code.                                                         |\n| formatCheck                  | Run &#96;gofmt&#96; on all the source code, reporting all differences.                                       |",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "---"
          },
          {
            "lineNumber": 2,
            "text": "title: Building Flamenco"
          },
          {
            "lineNumber": 3,
            "text": "weight: 10"
          },
          {
            "lineNumber": 4,
            "text": "---"
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "For the steps towards your first build, see [Getting Started][start]."
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "[start]: {{\u003c relref \"../getting-started \"\u003e}}"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "## Building with Magefile"
          },
          {
            "lineNumber": 11
          },
          {
            "lineNumber": 12,
            "text": "The Flamenco build tool is made in Go, using [Magefile][mage]."
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "[mage]: https://magefile.org/"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "### Basic Builds"
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "&#96;&#96;&#96;sh"
          },
          {
            "lineNumber": 19,
            "text": "$ go run mage.go build"
          },
          {
            "lineNumber": 20,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 21
          },
          {
            "lineNumber": 22,
            "text": "This builds Flamenco Manager, including its webapp, and Flamenco Worker."
          },
          {
            "lineNumber": 23
          },
          {
            "lineNumber": 24,
            "text": "### Listing Build Targets"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 27,
            "text": "$ go run mage.go -l"
          },
          {
            "lineNumber": 28,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 29
          },
          {
            "lineNumber": 30,
            "text": "Will list these targets:"
          },
          {
            "lineNumber": 31
          },
          {
            "lineNumber": 32,
            "text": "| Target                       | Description                                                                                          |"
          },
          {
            "lineNumber": 33,
            "text": "|------------------------------|------------------------------------------------------------------------------------------------------|"
          },
          {
            "lineNumber": 34,
            "text": "| build                        | Flamenco Manager and Flamenco Worker, including the webapp and the add-on                            |"
          },
          {
            "lineNumber": 35,
            "text": "| check                        | Run unit tests, check for vulnerabilities, and run the linter                                        |"
          },
          {
            "lineNumber": 36,
            "text": "| clean                        | Remove executables and other build output                                                            |"
          },
          {
            "lineNumber": 37,
            "text": "| devServerWebapp              |                                                                                                      |"
          },
          {
            "lineNumber": 38,
            "text": "| flamencoManager              | Build Flamenco Manager with the webapp and add-on ZIP embedded                                       |"
          },
          {
            "lineNumber": 39,
            "text": "| flamencoManagerRace          | Build the Flamenco Manager executable with race condition checker enabled, do not rebuild the webapp |"
          },
          {
            "lineNumber": 40,
            "text": "| flamencoManagerWithoutWebapp | Only build the Flamenco Manager executable, do not rebuild the webapp                                |"
          },
          {
            "lineNumber": 41,
            "text": "| flamencoWorker               | Build the Flamenco Worker executable                                                                 |"
          },
          {
            "lineNumber": 42,
            "text": "| format                       | Run &#96;gofmt&#96;, formatting all the source code.                                                         |"
          },
          {
            "lineNumber": 43,
            "text": "| formatCheck                  | Run &#96;gofmt&#96; on all the source code, reporting all differences.                                       |"
          }
        ],
        "range": {
          "endPosition": {
            "line": 43
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\building\\_index.md",
        "signatures": {}
      },
      "score": 0.29318320751190186
    },
    {
      "codeBlock": {
        "contents": "-include .env\n\nPKG := projects.blender.org/studio/flamenco\n\n# To update the version number in all the relevant places, update the VERSION\n# and RELEASE_CYCLE variables below and run &#96;make update-version&#96;.\nVERSION := 3.8.2\n# \"alpha\", \"beta\", \"rc\", or \"release\".\nRELEASE_CYCLE := release\n\n# _GIT_DESCRIPTION_OR_TAG is either something like '16-123abc' (when we're 16\n# commits since the last tag) or it's something like &#96;v3.0-beta2&#96; (when exactly\n# on a tagged version).\n_GIT_DESCRIPTION_OR_TAG := $(subst v${VERSION}-,,$(shell git describe --tag --dirty --always --abbrev=9))\n# In the above cases, GITHASH is either &#96;16-123abc&#96; (in the same case above) or\n# &#96;123abc&#96; (when the tag matches the current commit exactly) or &#96;dirty&#96; (when\n# the tag matches the current commit exactly, and there are subsequent\n# uncommitted changes). This is done to prevent repetition of the same tag\n# in the \"extended version\" of Flamenco, which combines ${VERSION} and\n# ${GITHASH}.\nGITHASH := $(subst v${VERSION},$(shell git rev-parse --short=9 HEAD),${_GIT_DESCRIPTION_OR_TAG})\nifeq (${GITHASH},dirty)\nGITHASH := $(shell git rev-parse --short=9 HEAD)\nendif\n\nBUILDTOOL := mage\nifeq ($(OS),Windows_NT)\n\tBUILDTOOL := $(BUILDTOOL).exe\nendif\nBUILDTOOL_PATH := ${PWD}/${BUILDTOOL}\n\n# Package name of the generated Python/JavaScript code for the Flamenco API.\nPY_API_PKG_NAME=flamenco.manager\nJS_API_PKG_NAME=manager\n\n# The directory that will contain the built webapp files, and some other files\n# that will be served as static files by the Flamenco Manager web server.\n#\n# WARNING: THIS IS USED IN &#96;rm -rf ${WEB_STATIC}&#96;, DO NOT MAKE EMPTY OR SET TO\n# ANY ABSOLUTE PATH.\nWEB_STATIC=web/static\n\n# The Hugo package + its version.\nHUGO_PKG := github.com/gohugoio/hugo@v0.121.2\n\n# Prevent any dependency that requires a C compiler, i.e. only work with pure-Go libraries.\nexport CGO_ENABLED=0\n\nall: application\n\n# Install generators and build the software.\nwith-deps: buildtool\n\t\"${BUILDTOOL_PATH}\" installGenerators",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "-include .env"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "PKG := projects.blender.org/studio/flamenco"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "# To update the version number in all the relevant places, update the VERSION"
          },
          {
            "lineNumber": 6,
            "text": "# and RELEASE_CYCLE variables below and run &#96;make update-version&#96;."
          },
          {
            "lineNumber": 7,
            "text": "VERSION := 3.8.2"
          },
          {
            "lineNumber": 8,
            "text": "# \"alpha\", \"beta\", \"rc\", or \"release\"."
          },
          {
            "lineNumber": 9,
            "text": "RELEASE_CYCLE := release"
          },
          {
            "lineNumber": 10
          },
          {
            "lineNumber": 11,
            "text": "# _GIT_DESCRIPTION_OR_TAG is either something like '16-123abc' (when we're 16"
          },
          {
            "lineNumber": 12,
            "text": "# commits since the last tag) or it's something like &#96;v3.0-beta2&#96; (when exactly"
          },
          {
            "lineNumber": 13,
            "text": "# on a tagged version)."
          },
          {
            "lineNumber": 14,
            "text": "_GIT_DESCRIPTION_OR_TAG := $(subst v${VERSION}-,,$(shell git describe --tag --dirty --always --abbrev=9))"
          },
          {
            "lineNumber": 15,
            "text": "# In the above cases, GITHASH is either &#96;16-123abc&#96; (in the same case above) or"
          },
          {
            "lineNumber": 16,
            "text": "# &#96;123abc&#96; (when the tag matches the current commit exactly) or &#96;dirty&#96; (when"
          },
          {
            "lineNumber": 17,
            "text": "# the tag matches the current commit exactly, and there are subsequent"
          },
          {
            "lineNumber": 18,
            "text": "# uncommitted changes). This is done to prevent repetition of the same tag"
          },
          {
            "lineNumber": 19,
            "text": "# in the \"extended version\" of Flamenco, which combines ${VERSION} and"
          },
          {
            "lineNumber": 20,
            "text": "# ${GITHASH}."
          },
          {
            "lineNumber": 21,
            "text": "GITHASH := $(subst v${VERSION},$(shell git rev-parse --short=9 HEAD),${_GIT_DESCRIPTION_OR_TAG})"
          },
          {
            "lineNumber": 22,
            "text": "ifeq (${GITHASH},dirty)"
          },
          {
            "lineNumber": 23,
            "text": "GITHASH := $(shell git rev-parse --short=9 HEAD)"
          },
          {
            "lineNumber": 24,
            "text": "endif"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "BUILDTOOL := mage"
          },
          {
            "lineNumber": 27,
            "text": "ifeq ($(OS),Windows_NT)"
          },
          {
            "lineNumber": 28,
            "text": "\tBUILDTOOL := $(BUILDTOOL).exe"
          },
          {
            "lineNumber": 29,
            "text": "endif"
          },
          {
            "lineNumber": 30,
            "text": "BUILDTOOL_PATH := ${PWD}/${BUILDTOOL}"
          },
          {
            "lineNumber": 31
          },
          {
            "lineNumber": 32,
            "text": "# Package name of the generated Python/JavaScript code for the Flamenco API."
          },
          {
            "lineNumber": 33,
            "text": "PY_API_PKG_NAME=flamenco.manager"
          },
          {
            "lineNumber": 34,
            "text": "JS_API_PKG_NAME=manager"
          },
          {
            "lineNumber": 35
          },
          {
            "lineNumber": 36,
            "text": "# The directory that will contain the built webapp files, and some other files"
          },
          {
            "lineNumber": 37,
            "text": "# that will be served as static files by the Flamenco Manager web server."
          },
          {
            "lineNumber": 38,
            "text": "#"
          },
          {
            "lineNumber": 39,
            "text": "# WARNING: THIS IS USED IN &#96;rm -rf ${WEB_STATIC}&#96;, DO NOT MAKE EMPTY OR SET TO"
          },
          {
            "lineNumber": 40,
            "text": "# ANY ABSOLUTE PATH."
          },
          {
            "lineNumber": 41,
            "text": "WEB_STATIC=web/static"
          },
          {
            "lineNumber": 42
          },
          {
            "lineNumber": 43,
            "text": "# The Hugo package + its version."
          },
          {
            "lineNumber": 44,
            "text": "HUGO_PKG := github.com/gohugoio/hugo@v0.121.2"
          },
          {
            "lineNumber": 45
          },
          {
            "lineNumber": 46,
            "text": "# Prevent any dependency that requires a C compiler, i.e. only work with pure-Go libraries."
          },
          {
            "lineNumber": 47,
            "text": "export CGO_ENABLED=0"
          },
          {
            "lineNumber": 48
          },
          {
            "lineNumber": 49,
            "text": "all: application"
          },
          {
            "lineNumber": 50
          },
          {
            "lineNumber": 51,
            "text": "# Install generators and build the software."
          },
          {
            "lineNumber": 52,
            "text": "with-deps: buildtool"
          },
          {
            "lineNumber": 53,
            "text": "\t\"${BUILDTOOL_PATH}\" installGenerators"
          }
        ],
        "range": {
          "endPosition": {
            "line": 53
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\Makefile",
        "signatures": {}
      },
      "score": 0.2920764684677124
    },
    {
      "codeBlock": {
        "contents": "| &#96;devserver-website&#96;               | Run the website locally, in a development server that monitors for file changes and auto-refreshes your browser.                                        |\n| &#96;devserver-webapp&#96;                | Run the Manager webapp locally in a development server.                                                                                                 |\n| &#96;generate&#96;                        | Generate the Go, Python, and JavaScript code.                                                                                                           |\n| &#96;generate-go&#96;                     | Generate the Go code, which includes OpenAPI code, as well as mocks for the unit tests.                                                                 |\n| &#96;generate-py&#96;                     | Generate the Python code, containing the OpenAPI client code for the Blender add-on.                                                                    |\n| &#96;generate-js&#96;                     | Generate the JavaScript code, containing the OpenAPI client code for the web interface.                                                                 |\n| &#96;test&#96;                            | Run the unit tests.                                                                                                                                     |\n| &#96;check&#96;                           | Run various checks on the Go code. This includes &#96;go vet&#96; and checks for known vulnerabilities.                                                         |\n| &#96;clean&#96;                           | Remove build-time files.                                                                                                                                |\n| &#96;version&#96;                         | Print some version numbers, mostly for debugging the Makefile itself.                                                                                   |\n| &#96;format&#96;                          | Run the auto-formatter on all Go code.                                                                                                                  |\n| &#96;format-check&#96;                    | Check that the Go source code is formatted correctly.                                                                                                   |",
        "detailedLines": [
          {
            "lineNumber": 83,
            "text": "| &#96;devserver-website&#96;               | Run the website locally, in a development server that monitors for file changes and auto-refreshes your browser.                                        |"
          },
          {
            "lineNumber": 84,
            "text": "| &#96;devserver-webapp&#96;                | Run the Manager webapp locally in a development server.                                                                                                 |"
          },
          {
            "lineNumber": 85,
            "text": "| &#96;generate&#96;                        | Generate the Go, Python, and JavaScript code.                                                                                                           |"
          },
          {
            "lineNumber": 86,
            "text": "| &#96;generate-go&#96;                     | Generate the Go code, which includes OpenAPI code, as well as mocks for the unit tests.                                                                 |"
          },
          {
            "lineNumber": 87,
            "text": "| &#96;generate-py&#96;                     | Generate the Python code, containing the OpenAPI client code for the Blender add-on.                                                                    |"
          },
          {
            "lineNumber": 88,
            "text": "| &#96;generate-js&#96;                     | Generate the JavaScript code, containing the OpenAPI client code for the web interface.                                                                 |"
          },
          {
            "lineNumber": 89,
            "text": "| &#96;test&#96;                            | Run the unit tests.                                                                                                                                     |"
          },
          {
            "lineNumber": 90,
            "text": "| &#96;check&#96;                           | Run various checks on the Go code. This includes &#96;go vet&#96; and checks for known vulnerabilities.                                                         |"
          },
          {
            "lineNumber": 91,
            "text": "| &#96;clean&#96;                           | Remove build-time files.                                                                                                                                |"
          },
          {
            "lineNumber": 92,
            "text": "| &#96;version&#96;                         | Print some version numbers, mostly for debugging the Makefile itself.                                                                                   |"
          },
          {
            "lineNumber": 93,
            "text": "| &#96;format&#96;                          | Run the auto-formatter on all Go code.                                                                                                                  |"
          },
          {
            "lineNumber": 94,
            "text": "| &#96;format-check&#96;                    | Check that the Go source code is formatted correctly.                                                                                                   |"
          }
        ],
        "range": {
          "endPosition": {
            "line": 94
          },
          "startPosition": {
            "line": 82
          }
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\building\\_index.md",
        "signatures": {}
      },
      "score": 0.28972411155700684
    },
    {
      "codeBlock": {
        "contents": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\n.DS_Store\ndist\ndist-ssr\ncoverage\n*.local\n\n/cypress/videos/\n/cypress/screenshots/\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# Logs"
          },
          {
            "lineNumber": 2,
            "text": "logs"
          },
          {
            "lineNumber": 3,
            "text": "*.log"
          },
          {
            "lineNumber": 4,
            "text": "npm-debug.log*"
          },
          {
            "lineNumber": 5,
            "text": "yarn-debug.log*"
          },
          {
            "lineNumber": 6,
            "text": "yarn-error.log*"
          },
          {
            "lineNumber": 7,
            "text": "pnpm-debug.log*"
          },
          {
            "lineNumber": 8,
            "text": "lerna-debug.log*"
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "node_modules"
          },
          {
            "lineNumber": 11,
            "text": ".DS_Store"
          },
          {
            "lineNumber": 12,
            "text": "dist"
          },
          {
            "lineNumber": 13,
            "text": "dist-ssr"
          },
          {
            "lineNumber": 14,
            "text": "coverage"
          },
          {
            "lineNumber": 15,
            "text": "*.local"
          },
          {
            "lineNumber": 16
          },
          {
            "lineNumber": 17,
            "text": "/cypress/videos/"
          },
          {
            "lineNumber": 18,
            "text": "/cypress/screenshots/"
          },
          {
            "lineNumber": 19
          },
          {
            "lineNumber": 20,
            "text": "# Editor directories and files"
          },
          {
            "lineNumber": 21,
            "text": ".vscode/*"
          },
          {
            "lineNumber": 22,
            "text": "!.vscode/extensions.json"
          },
          {
            "lineNumber": 23,
            "text": ".idea"
          },
          {
            "lineNumber": 24,
            "text": "*.suo"
          },
          {
            "lineNumber": 25,
            "text": "*.ntvs*"
          },
          {
            "lineNumber": 26,
            "text": "*.njsproj"
          },
          {
            "lineNumber": 27,
            "text": "*.sln"
          },
          {
            "lineNumber": 28,
            "text": "*.sw?"
          }
        ],
        "range": {
          "endPosition": {
            "line": 28
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\web\\app\\.gitignore",
        "signatures": {}
      },
      "score": 0.2889738082885742
    },
    {
      "codeBlock": {
        "contents": "deploy-website:\n\t$(MAKE) -s check-environment\n\trm -rf web/project-website/public/\n\tgo run ${HUGO_PKG} -s web/project-website --baseURL https://flamenco.blender.org/\n\trsync web/project-website/public/ ${WEBSERVER_SSH}:${WEBSERVER_ROOT}/ \\\n\t\t-e \"ssh -p ${WEBSERVER_SSH_PORT}\" \\\n\t\t-rl \\\n\t\t--exclude v2/ \\\n\t\t--exclude downloads/ \\\n\t\t--exclude .well-known/ \\\n\t\t--exclude .htaccess \\\n\t\t--delete-after\n\n# For production deployments: check variables stored in .env\n.PHONY: check-environment\ncheck-environment:\nifndef WEBSERVER_SSH\n\t@echo \"WEBSERVER_SSH not found. Check .env or .env.example\"\n\texit 1\nendif\nifndef WEBSERVER_ROOT\n\t@echo \"WEBSERVER_ROOT not found. Check .env or .env.example\"\n\texit 1\nendif\n\n\n# Download \u0026 install FFmpeg in the 'tools' directory for supported platforms.\n.PHONY: tools\ntools:\n\t$(MAKE) -s tools-linux\n\t$(MAKE) -s tools-darwin\n\t$(MAKE) -s tools-windows\n\n\n# FFmpeg version to bundle.\nFFMPEG_VERSION=7.0.2\nTOOLS=./tools\nTOOLS_DOWNLOAD=./tools/download\n\nFFMPEG_PACKAGE_LINUX=$(TOOLS_DOWNLOAD)/ffmpeg-$(FFMPEG_VERSION)-linux-amd64-static.tar.xz\nFFMPEG_PACKAGE_DARWIN=$(TOOLS_DOWNLOAD)/ffmpeg-$(FFMPEG_VERSION)-darwin-amd64.zip\nFFMPEG_PACKAGE_WINDOWS=$(TOOLS_DOWNLOAD)/ffmpeg-$(FFMPEG_VERSION)-windows-amd64.zip\n\n.PHONY: tools-linux\ntools-linux:\n\t[ -e $(FFMPEG_PACKAGE_LINUX) ] || curl \\\n\t\t--create-dirs -o $(FFMPEG_PACKAGE_LINUX) \\\n\t\thttps://www.johnvansickle.com/ffmpeg/releases/ffmpeg-$(FFMPEG_VERSION)-amd64-static.tar.xz\n\ttar xvf \\\n\t\t$(FFMPEG_PACKAGE_LINUX) \\\n\t\tffmpeg-$(FFMPEG_VERSION)-amd64-static/ffmpeg\n\tmv ffmpeg-$(FFMPEG_VERSION)-amd64-static/ffmpeg $(TOOLS)/ffmpeg-linux-amd64\n\trmdir ffmpeg-$(FFMPEG_VERSION)-amd64-static\n\n.PHONY: tools-darwin\ntools-darwin:\n\t[ -e $(FFMPEG_PACKAGE_DARWIN) ] || curl \\\n\t\t--create-dirs -o $(FFMPEG_PACKAGE_DARWIN) \\\n\t\thttps://evermeet.cx/ffmpeg/ffmpeg-$(FFMPEG_VERSION).zip\n\tunzip $(FFMPEG_PACKAGE_DARWIN)\n\tmv ffmpeg $(TOOLS)/ffmpeg-darwin-amd64\n\n.PHONY: tools-windows\ntools-windows:\n\t[ -e $(FFMPEG_PACKAGE_WINDOWS) ] || curl \\\n\t\t",
        "detailedLines": [
          {
            "lineNumber": 166,
            "text": "deploy-website:"
          },
          {
            "lineNumber": 167,
            "text": "\t$(MAKE) -s check-environment"
          },
          {
            "lineNumber": 168,
            "text": "\trm -rf web/project-website/public/"
          },
          {
            "lineNumber": 169,
            "text": "\tgo run ${HUGO_PKG} -s web/project-website --baseURL https://flamenco.blender.org/"
          },
          {
            "lineNumber": 170,
            "text": "\trsync web/project-website/public/ ${WEBSERVER_SSH}:${WEBSERVER_ROOT}/ \\"
          },
          {
            "lineNumber": 171,
            "text": "\t\t-e \"ssh -p ${WEBSERVER_SSH_PORT}\" \\"
          },
          {
            "lineNumber": 172,
            "text": "\t\t-rl \\"
          },
          {
            "lineNumber": 173,
            "text": "\t\t--exclude v2/ \\"
          },
          {
            "lineNumber": 174,
            "text": "\t\t--exclude downloads/ \\"
          },
          {
            "lineNumber": 175,
            "text": "\t\t--exclude .well-known/ \\"
          },
          {
            "lineNumber": 176,
            "text": "\t\t--exclude .htaccess \\"
          },
          {
            "lineNumber": 177,
            "text": "\t\t--delete-after"
          },
          {
            "lineNumber": 178
          },
          {
            "lineNumber": 179,
            "text": "# For production deployments: check variables stored in .env"
          },
          {
            "lineNumber": 180,
            "text": ".PHONY: check-environment"
          },
          {
            "lineNumber": 181,
            "text": "check-environment:"
          },
          {
            "lineNumber": 182,
            "text": "ifndef WEBSERVER_SSH"
          },
          {
            "lineNumber": 183,
            "text": "\t@echo \"WEBSERVER_SSH not found. Check .env or .env.example\""
          },
          {
            "lineNumber": 184,
            "text": "\texit 1"
          },
          {
            "lineNumber": 185,
            "text": "endif"
          },
          {
            "lineNumber": 186,
            "text": "ifndef WEBSERVER_ROOT"
          },
          {
            "lineNumber": 187,
            "text": "\t@echo \"WEBSERVER_ROOT not found. Check .env or .env.example\""
          },
          {
            "lineNumber": 188,
            "text": "\texit 1"
          },
          {
            "lineNumber": 189,
            "text": "endif"
          },
          {
            "lineNumber": 190
          },
          {
            "lineNumber": 191
          },
          {
            "lineNumber": 192,
            "text": "# Download \u0026 install FFmpeg in the 'tools' directory for supported platforms."
          },
          {
            "lineNumber": 193,
            "text": ".PHONY: tools"
          },
          {
            "lineNumber": 194,
            "text": "tools:"
          },
          {
            "lineNumber": 195,
            "text": "\t$(MAKE) -s tools-linux"
          },
          {
            "lineNumber": 196,
            "text": "\t$(MAKE) -s tools-darwin"
          },
          {
            "lineNumber": 197,
            "text": "\t$(MAKE) -s tools-windows"
          },
          {
            "lineNumber": 198
          },
          {
            "lineNumber": 199
          },
          {
            "lineNumber": 200,
            "text": "# FFmpeg version to bundle."
          },
          {
            "lineNumber": 201,
            "text": "FFMPEG_VERSION=7.0.2"
          },
          {
            "lineNumber": 202,
            "text": "TOOLS=./tools"
          },
          {
            "lineNumber": 203,
            "text": "TOOLS_DOWNLOAD=./tools/download"
          },
          {
            "lineNumber": 204
          },
          {
            "lineNumber": 205,
            "text": "FFMPEG_PACKAGE_LINUX=$(TOOLS_DOWNLOAD)/ffmpeg-$(FFMPEG_VERSION)-linux-amd64-static.tar.xz"
          },
          {
            "lineNumber": 206,
            "text": "FFMPEG_PACKAGE_DARWIN=$(TOOLS_DOWNLOAD)/ffmpeg-$(FFMPEG_VERSION)-darwin-amd64.zip"
          },
          {
            "lineNumber": 207,
            "text": "FFMPEG_PACKAGE_WINDOWS=$(TOOLS_DOWNLOAD)/ffmpeg-$(FFMPEG_VERSION)-windows-amd64.zip"
          },
          {
            "lineNumber": 208
          },
          {
            "lineNumber": 209,
            "text": ".PHONY: tools-linux"
          },
          {
            "lineNumber": 210,
            "text": "tools-linux:"
          },
          {
            "lineNumber": 211,
            "text": "\t[ -e $(FFMPEG_PACKAGE_LINUX) ] || curl \\"
          },
          {
            "lineNumber": 212,
            "text": "\t\t--create-dirs -o $(FFMPEG_PACKAGE_LINUX) \\"
          },
          {
            "lineNumber": 213,
            "text": "\t\thttps://www.johnvansickle.com/ffmpeg/releases/ffmpeg-$(FFMPEG_VERSION)-amd64-static.tar.xz"
          },
          {
            "lineNumber": 214,
            "text": "\ttar xvf \\"
          },
          {
            "lineNumber": 215,
            "text": "\t\t$(FFMPEG_PACKAGE_LINUX) \\"
          },
          {
            "lineNumber": 216,
            "text": "\t\tffmpeg-$(FFMPEG_VERSION)-amd64-static/ffmpeg"
          },
          {
            "lineNumber": 217,
            "text": "\tmv ffmpeg-$(FFMPEG_VERSION)-amd64-static/ffmpeg $(TOOLS)/ffmpeg-linux-amd64"
          },
          {
            "lineNumber": 218,
            "text": "\trmdir ffmpeg-$(FFMPEG_VERSION)-amd64-static"
          },
          {
            "lineNumber": 219
          },
          {
            "lineNumber": 220,
            "text": ".PHONY: tools-darwin"
          },
          {
            "lineNumber": 221,
            "text": "tools-darwin:"
          },
          {
            "lineNumber": 222,
            "text": "\t[ -e $(FFMPEG_PACKAGE_DARWIN) ] || curl \\"
          },
          {
            "lineNumber": 223,
            "text": "\t\t--create-dirs -o $(FFMPEG_PACKAGE_DARWIN) \\"
          },
          {
            "lineNumber": 224,
            "text": "\t\thttps://evermeet.cx/ffmpeg/ffmpeg-$(FFMPEG_VERSION).zip"
          },
          {
            "lineNumber": 225,
            "text": "\tunzip $(FFMPEG_PACKAGE_DARWIN)"
          },
          {
            "lineNumber": 226,
            "text": "\tmv ffmpeg $(TOOLS)/ffmpeg-darwin-amd64"
          },
          {
            "lineNumber": 227
          },
          {
            "lineNumber": 228,
            "text": ".PHONY: tools-windows"
          },
          {
            "lineNumber": 229,
            "text": "tools-windows:"
          },
          {
            "lineNumber": 230,
            "text": "\t[ -e $(FFMPEG_PACKAGE_WINDOWS) ] || curl \\"
          },
          {
            "lineNumber": 231,
            "text": "\t\t"
          }
        ],
        "range": {
          "endPosition": {
            "column": 2,
            "line": 230
          },
          "startPosition": {
            "line": 165
          }
        },
        "relativeWorkspacePath": ".\\Makefile",
        "signatures": {}
      },
      "score": 0.27867454290390015
    },
    {
      "codeBlock": {
        "contents": "| format                       | Run &#96;gofmt&#96;, formatting all the source code.                                                         |\n| formatCheck                  | Run &#96;gofmt&#96; on all the source code, reporting all differences.                                       |\n| generate                     | code (OpenAPI and test mocks)                                                                        |\n| generateGo                   | Generate Go code for Flamenco Manager and Worker                                                     |\n| generateJS                   | Generate JavaScript code for the webapp                                                              |\n| generatePy                   | Generate Python code for the add-on                                                                  |\n| govulncheck                  | Check for known vulnerabilities.                                                                     |\n| installDeps                  | Install build-time dependencies: code generators and NodeJS dependencies.                            |\n| installDepsWebapp            | Use Yarn to install the webapp's NodeJS dependencies                                                 |\n| installGenerators            | Install code generators.                                                                             |\n| staticcheck                  | Analyse the source code.                                                                             |\n| test                         | Run unit tests                                                                                       |\n| version                      | Show which version information would be embedded in executables                                      |\n| vet                          | Run &#96;go vet&#96;                                                                                         |\n| webappStatic                 | Build the webapp as static files that can be served                                                  |\n\n\n### Faster Re-builds\n\nThe above commands first build the build tool itself, and then run it. The build",
        "detailedLines": [
          {
            "lineNumber": 42,
            "text": "| format                       | Run &#96;gofmt&#96;, formatting all the source code.                                                         |"
          },
          {
            "lineNumber": 43,
            "text": "| formatCheck                  | Run &#96;gofmt&#96; on all the source code, reporting all differences.                                       |"
          },
          {
            "lineNumber": 44,
            "text": "| generate                     | code (OpenAPI and test mocks)                                                                        |"
          },
          {
            "lineNumber": 45,
            "text": "| generateGo                   | Generate Go code for Flamenco Manager and Worker                                                     |"
          },
          {
            "lineNumber": 46,
            "text": "| generateJS                   | Generate JavaScript code for the webapp                                                              |"
          },
          {
            "lineNumber": 47,
            "text": "| generatePy                   | Generate Python code for the add-on                                                                  |"
          },
          {
            "lineNumber": 48,
            "text": "| govulncheck                  | Check for known vulnerabilities.                                                                     |"
          },
          {
            "lineNumber": 49,
            "text": "| installDeps                  | Install build-time dependencies: code generators and NodeJS dependencies.                            |"
          },
          {
            "lineNumber": 50,
            "text": "| installDepsWebapp            | Use Yarn to install the webapp's NodeJS dependencies                                                 |"
          },
          {
            "lineNumber": 51,
            "text": "| installGenerators            | Install code generators.                                                                             |"
          },
          {
            "lineNumber": 52,
            "text": "| staticcheck                  | Analyse the source code.                                                                             |"
          },
          {
            "lineNumber": 53,
            "text": "| test                         | Run unit tests                                                                                       |"
          },
          {
            "lineNumber": 54,
            "text": "| version                      | Show which version information would be embedded in executables                                      |"
          },
          {
            "lineNumber": 55,
            "text": "| vet                          | Run &#96;go vet&#96;                                                                                         |"
          },
          {
            "lineNumber": 56,
            "text": "| webappStatic                 | Build the webapp as static files that can be served                                                  |"
          },
          {
            "lineNumber": 57
          },
          {
            "lineNumber": 58
          },
          {
            "lineNumber": 59,
            "text": "### Faster Re-builds"
          },
          {
            "lineNumber": 60
          },
          {
            "lineNumber": 61,
            "text": "The above commands first build the build tool itself, and then run it. The build"
          }
        ],
        "range": {
          "endPosition": {
            "line": 61
          },
          "startPosition": {
            "line": 41
          }
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\building\\_index.md",
        "signatures": {}
      },
      "score": 0.274924635887146
    },
    {
      "codeBlock": {
        "contents": "Install Node 22 via homebrew:\n\n&#96;&#96;&#96;\nbrew install node@22\nbrew link node@22\n&#96;&#96;&#96;\n\nThen install yarn:\n\n&#96;&#96;&#96;\nbrew install yarn\n&#96;&#96;&#96;\n\n{{\u003c /tab \u003e}}\n{{\u003c /tabs \u003e}}\n\n## 3. Your First Build\n\nRun &#96;go run mage.go installDeps&#96; to install build-time dependencies. This is\nonly necessary the first time you build Flamenco (or when these dependencies are\nupgraded, which is rare)\n\nBuild the application with &#96;go run mage.go build&#96;.\n\nYou should now have two executables: &#96;flamenco-manager&#96; and &#96;flamenco-worker&#96;.\nBoth can be run with the &#96;-help&#96; CLI argument to see the available options.\n\nSee [building][building] for more &#96;mage&#96; targets, for example to run unit tests,\nenable the race condition checker, and ways to speed up the build process.\n\n[building]: {{\u003c relref \"../building/\" \u003e}}\n\n## 4. Get Involved\n\nIf you're interested in helping out with Flamenco development, please read [Get Involved][get-involved]!\n\n[Blender's guidelines on contributing code][contributing] also applies to\nFlamenco. Be sure to give it a read-through, as it has useful information and\nwill make the whole process of getting your changes into Flamenco a more\npleasant one.\n\nIf you need to change or add any database queries, read through the [database section][database].\n\n[get-involved]: {{\u003cref \"development/get-involved\" \u003e}}\n[database]: {{\u003cref \"development/database\" \u003e}}\n[contributing]: https://developer.blender.org/docs/handbook/contributing/\n\n\n## Software Design\n\nThe Flamenco software follows an **API-first** approach. All the functionality\nof Flamenco Manager is exposed via [the OpenAPI interface][openapi] ([more\ninfo](openapi-info)). The web interface is no exception; anything you can do\nwith the web interface, you can do with any other OpenAPI client.\n\n- The API can be browsed by following the 'API' link in the top-right corner of\n  the Flamenco Manager web interface. That's a link to\n  &#96;http://your.manager.address/api/v3/swagger-ui/&#96;\n- The web interface, Flamenco Worker, and the Blender add-on are all using that\n  same API.\n\n[openapi]: https://projects.blender.org/studio/flamenco/src/branch/main/pkg/api/flamenco-openapi.yaml",
        "detailedLines": [
          {
            "lineNumber": 72,
            "text": "Install Node 22 via homebrew:"
          },
          {
            "lineNumber": 73
          },
          {
            "lineNumber": 74,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 75,
            "text": "brew install node@22"
          },
          {
            "lineNumber": 76,
            "text": "brew link node@22"
          },
          {
            "lineNumber": 77,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "Then install yarn:"
          },
          {
            "lineNumber": 80
          },
          {
            "lineNumber": 81,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 82,
            "text": "brew install yarn"
          },
          {
            "lineNumber": 83,
            "text": "&#96;&#96;&#96;"
          },
          {
            "lineNumber": 84
          },
          {
            "lineNumber": 85,
            "text": "{{\u003c /tab \u003e}}"
          },
          {
            "lineNumber": 86,
            "text": "{{\u003c /tabs \u003e}}"
          },
          {
            "lineNumber": 87
          },
          {
            "lineNumber": 88,
            "text": "## 3. Your First Build"
          },
          {
            "lineNumber": 89
          },
          {
            "lineNumber": 90,
            "text": "Run &#96;go run mage.go installDeps&#96; to install build-time dependencies. This is"
          },
          {
            "lineNumber": 91,
            "text": "only necessary the first time you build Flamenco (or when these dependencies are"
          },
          {
            "lineNumber": 92,
            "text": "upgraded, which is rare)"
          },
          {
            "lineNumber": 93
          },
          {
            "lineNumber": 94,
            "text": "Build the application with &#96;go run mage.go build&#96;."
          },
          {
            "lineNumber": 95
          },
          {
            "lineNumber": 96,
            "text": "You should now have two executables: &#96;flamenco-manager&#96; and &#96;flamenco-worker&#96;."
          },
          {
            "lineNumber": 97,
            "text": "Both can be run with the &#96;-help&#96; CLI argument to see the available options."
          },
          {
            "lineNumber": 98
          },
          {
            "lineNumber": 99,
            "text": "See [building][building] for more &#96;mage&#96; targets, for example to run unit tests,"
          },
          {
            "lineNumber": 100,
            "text": "enable the race condition checker, and ways to speed up the build process."
          },
          {
            "lineNumber": 101
          },
          {
            "lineNumber": 102,
            "text": "[building]: {{\u003c relref \"../building/\" \u003e}}"
          },
          {
            "lineNumber": 103
          },
          {
            "lineNumber": 104,
            "text": "## 4. Get Involved"
          },
          {
            "lineNumber": 105
          },
          {
            "lineNumber": 106,
            "text": "If you're interested in helping out with Flamenco development, please read [Get Involved][get-involved]!"
          },
          {
            "lineNumber": 107
          },
          {
            "lineNumber": 108,
            "text": "[Blender's guidelines on contributing code][contributing] also applies to"
          },
          {
            "lineNumber": 109,
            "text": "Flamenco. Be sure to give it a read-through, as it has useful information and"
          },
          {
            "lineNumber": 110,
            "text": "will make the whole process of getting your changes into Flamenco a more"
          },
          {
            "lineNumber": 111,
            "text": "pleasant one."
          },
          {
            "lineNumber": 112
          },
          {
            "lineNumber": 113,
            "text": "If you need to change or add any database queries, read through the [database section][database]."
          },
          {
            "lineNumber": 114
          },
          {
            "lineNumber": 115,
            "text": "[get-involved]: {{\u003cref \"development/get-involved\" \u003e}}"
          },
          {
            "lineNumber": 116,
            "text": "[database]: {{\u003cref \"development/database\" \u003e}}"
          },
          {
            "lineNumber": 117,
            "text": "[contributing]: https://developer.blender.org/docs/handbook/contributing/"
          },
          {
            "lineNumber": 118
          },
          {
            "lineNumber": 119
          },
          {
            "lineNumber": 120,
            "text": "## Software Design"
          },
          {
            "lineNumber": 121
          },
          {
            "lineNumber": 122,
            "text": "The Flamenco software follows an **API-first** approach. All the functionality"
          },
          {
            "lineNumber": 123,
            "text": "of Flamenco Manager is exposed via [the OpenAPI interface][openapi] ([more"
          },
          {
            "lineNumber": 124,
            "text": "info](openapi-info)). The web interface is no exception; anything you can do"
          },
          {
            "lineNumber": 125,
            "text": "with the web interface, you can do with any other OpenAPI client."
          },
          {
            "lineNumber": 126
          },
          {
            "lineNumber": 127,
            "text": "- The API can be browsed by following the 'API' link in the top-right corner of"
          },
          {
            "lineNumber": 128,
            "text": "  the Flamenco Manager web interface. That's a link to"
          },
          {
            "lineNumber": 129,
            "text": "  &#96;http://your.manager.address/api/v3/swagger-ui/&#96;"
          },
          {
            "lineNumber": 130,
            "text": "- The web interface, Flamenco Worker, and the Blender add-on are all using that"
          },
          {
            "lineNumber": 131,
            "text": "  same API."
          },
          {
            "lineNumber": 132
          },
          {
            "lineNumber": 133,
            "text": "[openapi]: https://projects.blender.org/studio/flamenco/src/branch/main/pkg/api/flamenco-openapi.yaml"
          }
        ],
        "range": {
          "endPosition": {
            "line": 133
          },
          "startPosition": {
            "line": 71
          }
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\getting-started\\_index.md",
        "signatures": {}
      },
      "score": 0.2732110917568207
    },
    {
      "codeBlock": {
        "contents": "module.exports = {\n  \"root\": true,\n  \"extends\": [\n    \"plugin:vue/vue3-essential\",\n    \"eslint:recommended\",\n    \"@vue/eslint-config-prettier\"\n  ],\n  \"env\": {\n    \"vue/setup-compiler-macros\": true\n  }\n}",
        "detailedLines": [
          {
            "lineNumber": 4,
            "text": "module.exports = {"
          },
          {
            "lineNumber": 5,
            "text": "  \"root\": true,"
          },
          {
            "lineNumber": 6,
            "text": "  \"extends\": ["
          },
          {
            "lineNumber": 7,
            "text": "    \"plugin:vue/vue3-essential\","
          },
          {
            "lineNumber": 8,
            "text": "    \"eslint:recommended\","
          },
          {
            "lineNumber": 9,
            "text": "    \"@vue/eslint-config-prettier\""
          },
          {
            "lineNumber": 10,
            "text": "  ],"
          },
          {
            "lineNumber": 11,
            "text": "  \"env\": {"
          },
          {
            "lineNumber": 12,
            "text": "    \"vue/setup-compiler-macros\": true"
          },
          {
            "lineNumber": 13,
            "text": "  }"
          },
          {
            "lineNumber": 14,
            "text": "}"
          }
        ],
        "range": {
          "endPosition": {
            "column": 1,
            "line": 13
          },
          "startPosition": {
            "column": 60,
            "line": 1
          }
        },
        "relativeWorkspacePath": ".\\web\\app\\.eslintrc.cjs",
        "signatures": {}
      },
      "score": 0.2712441682815552
    },
    {
      "codeBlock": {
        "contents": "//go:build mage\n\npackage main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/magefile/mage/mg\"\n\t\"github.com/magefile/mage/sh\"\n\t\"golang.org/x/vuln/scan\"\n\t\"honnef.co/go/tools/lintcmd\"\n\tlintcmdversion \"honnef.co/go/tools/lintcmd/version\"\n\t\"honnef.co/go/tools/simple\"\n\t\"honnef.co/go/tools/staticcheck\"\n\t\"honnef.co/go/tools/stylecheck\"\n\t\"honnef.co/go/tools/unused\"\n)\n\n// Run unit tests, check for vulnerabilities, and run the linter\nfunc Check(ctx context.Context) {\n\tmg.CtxDeps(ctx, Test, Govulncheck, Staticcheck, Vet, FormatCheck)\n}\n\n// Run unit tests\nfunc Test(ctx context.Context) error {\n\treturn sh.RunV(mg.GoCmd(), \"test\", \"-short\", \"-failfast\", \"./...\")\n}\n\n// Check for known vulnerabilities.\nfunc Govulncheck(ctx context.Context) error {\n\tcmd := scan.Command(ctx, \"./...\")\n\tif err := cmd.Start(); err != nil {\n\t\treturn err\n\t}\n\treturn cmd.Wait()\n}\n\n// Analyse the source code.\nfunc Staticcheck() error {\n\tcmd := lintcmd.NewCommand(\"staticcheck\")\n\tcmd.SetVersion(lintcmdversion.Version, lintcmdversion.MachineVersion)\n\tcmd.ParseFlags([]string{\"./...\"})\n\tcmd.AddAnalyzers(simple.Analyzers...)\n\tcmd.AddAnalyzers(staticcheck.Analyzers...)\n\tcmd.AddAnalyzers(stylecheck.Analyzers...)\n\tcmd.AddAnalyzers(unused.Analyzer)\n\n\texitCode := cmd.Execute()\n\tif exitCode != 0 {\n\t\treturn errors.New(\"staticcheck failed\")\n\t}\n\tfmt.Println(\"staticcheck ok\")\n\treturn nil\n}\n\n// Run &#96;go vet&#96;\nfunc Vet() error {\n\treturn sh.RunV(mg.GoCmd(), \"vet\", \"./...\")\n}\n\n// Run &#96;gofmt&#96;, formatting all the source code.\nfunc Format() error {\n\treturn sh.RunV(\"gofmt\", \"-s\", \"-w\", \".\")\n}\n\n// Run &#96;gofmt&#96; on all the source code, reporting all differences.\nfunc FormatCheck(ctx context.Context) error {\n\toutput, err := sh.Output(\"gofmt\", \"-d\", \".\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif output == \"\" {\n\t\t// Format was OK.\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"Formatting check failed:\\n%s\", output)\n}",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "//go:build mage"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "package main"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "import ("
          },
          {
            "lineNumber": 6,
            "text": "\t\"context\""
          },
          {
            "lineNumber": 7,
            "text": "\t\"errors\""
          },
          {
            "lineNumber": 8,
            "text": "\t\"fmt\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "\t\"github.com/magefile/mage/mg\""
          },
          {
            "lineNumber": 11,
            "text": "\t\"github.com/magefile/mage/sh\""
          },
          {
            "lineNumber": 12,
            "text": "\t\"golang.org/x/vuln/scan\""
          },
          {
            "lineNumber": 13,
            "text": "\t\"honnef.co/go/tools/lintcmd\""
          },
          {
            "lineNumber": 14,
            "text": "\tlintcmdversion \"honnef.co/go/tools/lintcmd/version\""
          },
          {
            "lineNumber": 15,
            "text": "\t\"honnef.co/go/tools/simple\""
          },
          {
            "lineNumber": 16,
            "text": "\t\"honnef.co/go/tools/staticcheck\""
          },
          {
            "lineNumber": 17,
            "text": "\t\"honnef.co/go/tools/stylecheck\""
          },
          {
            "lineNumber": 18,
            "text": "\t\"honnef.co/go/tools/unused\""
          },
          {
            "lineNumber": 19,
            "text": ")"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21,
            "text": "// Run unit tests, check for vulnerabilities, and run the linter"
          },
          {
            "lineNumber": 22,
            "text": "func Check(ctx context.Context) {"
          },
          {
            "lineNumber": 23,
            "text": "\tmg.CtxDeps(ctx, Test, Govulncheck, Staticcheck, Vet, FormatCheck)"
          },
          {
            "lineNumber": 24,
            "text": "}"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "// Run unit tests"
          },
          {
            "lineNumber": 27,
            "text": "func Test(ctx context.Context) error {"
          },
          {
            "lineNumber": 28,
            "text": "\treturn sh.RunV(mg.GoCmd(), \"test\", \"-short\", \"-failfast\", \"./...\")"
          },
          {
            "lineNumber": 29,
            "text": "}"
          },
          {
            "lineNumber": 30
          },
          {
            "lineNumber": 31,
            "text": "// Check for known vulnerabilities."
          },
          {
            "lineNumber": 32,
            "text": "func Govulncheck(ctx context.Context) error {"
          },
          {
            "lineNumber": 33,
            "text": "\tcmd := scan.Command(ctx, \"./...\")"
          },
          {
            "lineNumber": 34,
            "text": "\tif err := cmd.Start(); err != nil {"
          },
          {
            "lineNumber": 35,
            "text": "\t\treturn err"
          },
          {
            "lineNumber": 36,
            "text": "\t}"
          },
          {
            "lineNumber": 37,
            "text": "\treturn cmd.Wait()"
          },
          {
            "lineNumber": 38,
            "text": "}"
          },
          {
            "lineNumber": 39
          },
          {
            "lineNumber": 40,
            "text": "// Analyse the source code."
          },
          {
            "lineNumber": 41,
            "text": "func Staticcheck() error {"
          },
          {
            "lineNumber": 42,
            "text": "\tcmd := lintcmd.NewCommand(\"staticcheck\")"
          },
          {
            "lineNumber": 43,
            "text": "\tcmd.SetVersion(lintcmdversion.Version, lintcmdversion.MachineVersion)"
          },
          {
            "lineNumber": 44,
            "text": "\tcmd.ParseFlags([]string{\"./...\"})"
          },
          {
            "lineNumber": 45,
            "text": "\tcmd.AddAnalyzers(simple.Analyzers...)"
          },
          {
            "lineNumber": 46,
            "text": "\tcmd.AddAnalyzers(staticcheck.Analyzers...)"
          },
          {
            "lineNumber": 47,
            "text": "\tcmd.AddAnalyzers(stylecheck.Analyzers...)"
          },
          {
            "lineNumber": 48,
            "text": "\tcmd.AddAnalyzers(unused.Analyzer)"
          },
          {
            "lineNumber": 49
          },
          {
            "lineNumber": 50,
            "text": "\texitCode := cmd.Execute()"
          },
          {
            "lineNumber": 51,
            "text": "\tif exitCode != 0 {"
          },
          {
            "lineNumber": 52,
            "text": "\t\treturn errors.New(\"staticcheck failed\")"
          },
          {
            "lineNumber": 53,
            "text": "\t}"
          },
          {
            "lineNumber": 54,
            "text": "\tfmt.Println(\"staticcheck ok\")"
          },
          {
            "lineNumber": 55,
            "text": "\treturn nil"
          },
          {
            "lineNumber": 56,
            "text": "}"
          },
          {
            "lineNumber": 57
          },
          {
            "lineNumber": 58,
            "text": "// Run &#96;go vet&#96;"
          },
          {
            "lineNumber": 59,
            "text": "func Vet() error {"
          },
          {
            "lineNumber": 60,
            "text": "\treturn sh.RunV(mg.GoCmd(), \"vet\", \"./...\")"
          },
          {
            "lineNumber": 61,
            "text": "}"
          },
          {
            "lineNumber": 62
          },
          {
            "lineNumber": 63,
            "text": "// Run &#96;gofmt&#96;, formatting all the source code."
          },
          {
            "lineNumber": 64,
            "text": "func Format() error {"
          },
          {
            "lineNumber": 65,
            "text": "\treturn sh.RunV(\"gofmt\", \"-s\", \"-w\", \".\")"
          },
          {
            "lineNumber": 66,
            "text": "}"
          },
          {
            "lineNumber": 67
          },
          {
            "lineNumber": 68,
            "text": "// Run &#96;gofmt&#96; on all the source code, reporting all differences."
          },
          {
            "lineNumber": 69,
            "text": "func FormatCheck(ctx context.Context) error {"
          },
          {
            "lineNumber": 70,
            "text": "\toutput, err := sh.Output(\"gofmt\", \"-d\", \".\")"
          },
          {
            "lineNumber": 71,
            "text": "\tif err != nil {"
          },
          {
            "lineNumber": 72,
            "text": "\t\treturn err"
          },
          {
            "lineNumber": 73,
            "text": "\t}"
          },
          {
            "lineNumber": 74
          },
          {
            "lineNumber": 75,
            "text": "\tif output == \"\" {"
          },
          {
            "lineNumber": 76,
            "text": "\t\t// Format was OK."
          },
          {
            "lineNumber": 77,
            "text": "\t\treturn nil"
          },
          {
            "lineNumber": 78,
            "text": "\t}"
          },
          {
            "lineNumber": 79
          },
          {
            "lineNumber": 80,
            "text": "\treturn fmt.Errorf(\"Formatting check failed:\\n%s\", output)"
          },
          {
            "lineNumber": 81,
            "text": "}"
          }
        ],
        "range": {
          "endPosition": {
            "column": 1,
            "line": 80
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\magefiles\\check.go",
        "signatures": {}
      },
      "score": 0.26606398820877075
    },
    {
      "codeBlock": {
        "contents": "with-deps: buildtool\n\t\"${BUILDTOOL_PATH}\" installGenerators\n\t$(MAKE) application\n\nvet: buildtool\n\t\"${BUILDTOOL_PATH}\" vet\n\ncheck: buildtool\n\t\"${BUILDTOOL_PATH}\" check\n\napplication: flamenco-manager flamenco-worker\n\nflamenco-manager: buildtool\n\t\"${BUILDTOOL_PATH}\" flamencoManager\n\nflamenco-manager-without-webapp: buildtool\n\t\"${BUILDTOOL_PATH}\" flamencoManagerWithoutWebapp\n\nflamenco-worker: buildtool\n\t\"${BUILDTOOL_PATH}\" flamencoWorker\n\n# Builds the buildtool itself, for faster rebuilds of Skyfill.\nbuildtool: ${BUILDTOOL}\n${BUILDTOOL}: mage.go $(wildcard magefiles/*.go) go.mod\n\t@echo \"Building build tool $@\"\n\t@go run mage.go -compile \"${BUILDTOOL_PATH}\"\n\n# NOTE: these database migration commands are just for reference / debugging /\n# development purposes. Flamenco Manager and Worker each perform their own\n# migration at startup. In normal use, you'll never need those commands.\ndb-migrate-status:\n\tgo tool goose -dir ./internal/manager/persistence/migrations/ sqlite3 flamenco-manager.sqlite status\ndb-migrate-up:\n\tgo tool goose -dir ./internal/manager/persistence/migrations/ sqlite3 flamenco-manager.sqlite up\ndb-migrate-down:\n\tgo tool goose -dir ./internal/manager/persistence/migrations/ sqlite3 flamenco-manager.sqlite down\n.PHONY: db-migrate-status db-migrate-up db-migrate-down\n\nwebapp-static: buildtool\n\t\"${BUILDTOOL_PATH}\" webappStatic\n\ninstall-generators: buildtool\n\t\"${BUILDTOOL_PATH}\" installGenerators\n\ngenerate: buildtool\n\t\"${BUILDTOOL_PATH}\" generate\n\ngenerate-go: buildtool\n\t\"${BUILDTOOL_PATH}\" generateGo\n\ngenerate-py: buildtool\n\t\"${BUILDTOOL_PATH}\" generatePy\n\ngenerate-js: buildtool\n\t\"${BUILDTOOL_PATH}\" generateJS\n\n.PHONY:\nupdate-version:\n\t@echo \"--- Updating Flamenco version to ${VERSION}\"",
        "detailedLines": [
          {
            "lineNumber": 52,
            "text": "with-deps: buildtool"
          },
          {
            "lineNumber": 53,
            "text": "\t\"${BUILDTOOL_PATH}\" installGenerators"
          },
          {
            "lineNumber": 54,
            "text": "\t$(MAKE) application"
          },
          {
            "lineNumber": 55
          },
          {
            "lineNumber": 56,
            "text": "vet: buildtool"
          },
          {
            "lineNumber": 57,
            "text": "\t\"${BUILDTOOL_PATH}\" vet"
          },
          {
            "lineNumber": 58
          },
          {
            "lineNumber": 59,
            "text": "check: buildtool"
          },
          {
            "lineNumber": 60,
            "text": "\t\"${BUILDTOOL_PATH}\" check"
          },
          {
            "lineNumber": 61
          },
          {
            "lineNumber": 62,
            "text": "application: flamenco-manager flamenco-worker"
          },
          {
            "lineNumber": 63
          },
          {
            "lineNumber": 64,
            "text": "flamenco-manager: buildtool"
          },
          {
            "lineNumber": 65,
            "text": "\t\"${BUILDTOOL_PATH}\" flamencoManager"
          },
          {
            "lineNumber": 66
          },
          {
            "lineNumber": 67,
            "text": "flamenco-manager-without-webapp: buildtool"
          },
          {
            "lineNumber": 68,
            "text": "\t\"${BUILDTOOL_PATH}\" flamencoManagerWithoutWebapp"
          },
          {
            "lineNumber": 69
          },
          {
            "lineNumber": 70,
            "text": "flamenco-worker: buildtool"
          },
          {
            "lineNumber": 71,
            "text": "\t\"${BUILDTOOL_PATH}\" flamencoWorker"
          },
          {
            "lineNumber": 72
          },
          {
            "lineNumber": 73,
            "text": "# Builds the buildtool itself, for faster rebuilds of Skyfill."
          },
          {
            "lineNumber": 74,
            "text": "buildtool: ${BUILDTOOL}"
          },
          {
            "lineNumber": 75,
            "text": "${BUILDTOOL}: mage.go $(wildcard magefiles/*.go) go.mod"
          },
          {
            "lineNumber": 76,
            "text": "\t@echo \"Building build tool $@\""
          },
          {
            "lineNumber": 77,
            "text": "\t@go run mage.go -compile \"${BUILDTOOL_PATH}\""
          },
          {
            "lineNumber": 78
          },
          {
            "lineNumber": 79,
            "text": "# NOTE: these database migration commands are just for reference / debugging /"
          },
          {
            "lineNumber": 80,
            "text": "# development purposes. Flamenco Manager and Worker each perform their own"
          },
          {
            "lineNumber": 81,
            "text": "# migration at startup. In normal use, you'll never need those commands."
          },
          {
            "lineNumber": 82,
            "text": "db-migrate-status:"
          },
          {
            "lineNumber": 83,
            "text": "\tgo tool goose -dir ./internal/manager/persistence/migrations/ sqlite3 flamenco-manager.sqlite status"
          },
          {
            "lineNumber": 84,
            "text": "db-migrate-up:"
          },
          {
            "lineNumber": 85,
            "text": "\tgo tool goose -dir ./internal/manager/persistence/migrations/ sqlite3 flamenco-manager.sqlite up"
          },
          {
            "lineNumber": 86,
            "text": "db-migrate-down:"
          },
          {
            "lineNumber": 87,
            "text": "\tgo tool goose -dir ./internal/manager/persistence/migrations/ sqlite3 flamenco-manager.sqlite down"
          },
          {
            "lineNumber": 88,
            "text": ".PHONY: db-migrate-status db-migrate-up db-migrate-down"
          },
          {
            "lineNumber": 89
          },
          {
            "lineNumber": 90,
            "text": "webapp-static: buildtool"
          },
          {
            "lineNumber": 91,
            "text": "\t\"${BUILDTOOL_PATH}\" webappStatic"
          },
          {
            "lineNumber": 92
          },
          {
            "lineNumber": 93,
            "text": "install-generators: buildtool"
          },
          {
            "lineNumber": 94,
            "text": "\t\"${BUILDTOOL_PATH}\" installGenerators"
          },
          {
            "lineNumber": 95
          },
          {
            "lineNumber": 96,
            "text": "generate: buildtool"
          },
          {
            "lineNumber": 97,
            "text": "\t\"${BUILDTOOL_PATH}\" generate"
          },
          {
            "lineNumber": 98
          },
          {
            "lineNumber": 99,
            "text": "generate-go: buildtool"
          },
          {
            "lineNumber": 100,
            "text": "\t\"${BUILDTOOL_PATH}\" generateGo"
          },
          {
            "lineNumber": 101
          },
          {
            "lineNumber": 102,
            "text": "generate-py: buildtool"
          },
          {
            "lineNumber": 103,
            "text": "\t\"${BUILDTOOL_PATH}\" generatePy"
          },
          {
            "lineNumber": 104
          },
          {
            "lineNumber": 105,
            "text": "generate-js: buildtool"
          },
          {
            "lineNumber": 106,
            "text": "\t\"${BUILDTOOL_PATH}\" generateJS"
          },
          {
            "lineNumber": 107
          },
          {
            "lineNumber": 108,
            "text": ".PHONY:"
          },
          {
            "lineNumber": 109,
            "text": "update-version:"
          },
          {
            "lineNumber": 110,
            "text": "\t@echo \"--- Updating Flamenco version to ${VERSION}\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 110
          },
          "startPosition": {
            "line": 51
          }
        },
        "relativeWorkspacePath": ".\\Makefile",
        "signatures": {}
      },
      "score": 0.2653214931488037
    },
    {
      "codeBlock": {
        "contents": "| &#96;format&#96;                          | Run the auto-formatter on all Go code.                                                                                                                  |\n| &#96;format-check&#96;                    | Check that the Go source code is formatted correctly.                                                                                                   |\n| &#96;list-embedded&#96;                   | List the files embedded into the &#96;flamenco-manager&#96; executable.                                                                                         |\n| &#96;tools&#96;                           | Download FFmpeg for all supported platforms. Can be suffixed by &#96;-linux&#96;, &#96;-windows&#96;, or &#96;-darwin&#96; to only download for specific platforms.             |\n| &#96;update-version&#96;                  | Takes the &#96;VERSION&#96; and &#96;RELEASE_CYCLE&#96; declared at the top of the &#96;Makefile&#96; and uses that to update various source files.                             |\n| &#96;release-package&#96;                 | Builds release packages for all supported platforms. Can be suffixed by &#96;-linux&#96;, &#96;-windows&#96;, or &#96;-darwin&#96; to only build specific platforms.            |\n| &#96;db-migrate-status&#96;               | Database migration: show the current version of the database schema.                                                                                    |\n| &#96;db-migrate-up&#96;                   | Database migration: perform one migration step towards the latest version.                                                                              |\n| &#96;db-migrate-down&#96;                 | Database migration: roll back one migration step, so go to an older version. This may not be lossless.                                                  |",
        "detailedLines": [
          {
            "lineNumber": 93,
            "text": "| &#96;format&#96;                          | Run the auto-formatter on all Go code.                                                                                                                  |"
          },
          {
            "lineNumber": 94,
            "text": "| &#96;format-check&#96;                    | Check that the Go source code is formatted correctly.                                                                                                   |"
          },
          {
            "lineNumber": 95,
            "text": "| &#96;list-embedded&#96;                   | List the files embedded into the &#96;flamenco-manager&#96; executable.                                                                                         |"
          },
          {
            "lineNumber": 96,
            "text": "| &#96;tools&#96;                           | Download FFmpeg for all supported platforms. Can be suffixed by &#96;-linux&#96;, &#96;-windows&#96;, or &#96;-darwin&#96; to only download for specific platforms.             |"
          },
          {
            "lineNumber": 97,
            "text": "| &#96;update-version&#96;                  | Takes the &#96;VERSION&#96; and &#96;RELEASE_CYCLE&#96; declared at the top of the &#96;Makefile&#96; and uses that to update various source files.                             |"
          },
          {
            "lineNumber": 98,
            "text": "| &#96;release-package&#96;                 | Builds release packages for all supported platforms. Can be suffixed by &#96;-linux&#96;, &#96;-windows&#96;, or &#96;-darwin&#96; to only build specific platforms.            |"
          },
          {
            "lineNumber": 99,
            "text": "| &#96;db-migrate-status&#96;               | Database migration: show the current version of the database schema.                                                                                    |"
          },
          {
            "lineNumber": 100,
            "text": "| &#96;db-migrate-up&#96;                   | Database migration: perform one migration step towards the latest version.                                                                              |"
          },
          {
            "lineNumber": 101,
            "text": "| &#96;db-migrate-down&#96;                 | Database migration: roll back one migration step, so go to an older version. This may not be lossless.                                                  |"
          }
        ],
        "range": {
          "endPosition": {
            "line": 101
          },
          "startPosition": {
            "line": 92
          }
        },
        "relativeWorkspacePath": ".\\web\\project-website\\content\\development\\building\\_index.md",
        "signatures": {}
      },
      "score": 0.2590310573577881
    },
    {
      "codeBlock": {
        "contents": "update-version:\n\t@echo \"--- Updating Flamenco version to ${VERSION}\"\n\t@echo \"--- If this stops with exit status 42, it was already at that version.\"\n\t@echo\n\tgo run ./cmd/update-version ${VERSION} ${RELEASE_CYCLE}\n\t$(MAKE) generate-py\n\t$(MAKE) generate-js\n\t@echo\n\t@echo 'File replacement done, commit with:'\n\t@echo\n\t@echo git commit -m '\"Bumped version to ${VERSION}\"' Makefile \\\n\t\taddon/flamenco/__init__.py \\\n\t\taddon/flamenco/manager \\\n\t\taddon/flamenco/manager_README.md \\\n\t\tmagefiles/version.go \\\n\t\tweb/app/src/manager-api \\\n\t\tweb/project-website/data/flamenco.yaml\n\t@echo 'git tag -a -m \"Tagged version ${VERSION}\" v${VERSION}'\n\nversion:\n\t@echo \"Package     : ${PKG}\"\n\t@echo \"Version     : ${VERSION}\"\n\t@echo \"Git Hash    : ${GITHASH}\"\n\t@echo -n \"GOOS        : \"; go env GOOS\n\t@echo -n \"GOARCH      : \"; go env GOARCH\n\t@echo\n\t@env | grep GO\n\nlist-embedded:\n\t@go list -f '{{printf \"%10s\" .Name}}: {{.EmbedFiles}}' ${PKG}/...\n\nswagger-ui:\n\tgit clone --depth 1 https://github.com/swagger-api/swagger-ui.git tmp-swagger-ui\n\trm -rf pkg/api/static/swagger-ui\n\tmv tmp-swagger-ui/dist pkg/api/static/swagger-ui\n\trm -rf tmp-swagger-ui\n\t@echo\n\t@echo 'Now update pkg/api/static/swagger-ui/index.html to have url: \"/api/openapi3.json\",'\n\ntest: buildtool\n\t\"${BUILDTOOL_PATH}\" test\n\nclean: buildtool\n\t\"${BUILDTOOL_PATH}\" clean\n\ndevserver-website:\n\tgo run ${HUGO_PKG} -s web/project-website serve\n\ndevserver-webapp: buildtool\n\t\"${BUILDTOOL_PATH}\" devServerWebapp\n\nformat: buildtool\n\t\"${BUILDTOOL_PATH}\" format\n\nformat-check: buildtool\n\t\"${BUILDTOOL_PATH}\" formatCheck\n\ndeploy-website:\n\t$(MAKE) -s check-environment",
        "detailedLines": [
          {
            "lineNumber": 109,
            "text": "update-version:"
          },
          {
            "lineNumber": 110,
            "text": "\t@echo \"--- Updating Flamenco version to ${VERSION}\""
          },
          {
            "lineNumber": 111,
            "text": "\t@echo \"--- If this stops with exit status 42, it was already at that version.\""
          },
          {
            "lineNumber": 112,
            "text": "\t@echo"
          },
          {
            "lineNumber": 113,
            "text": "\tgo run ./cmd/update-version ${VERSION} ${RELEASE_CYCLE}"
          },
          {
            "lineNumber": 114,
            "text": "\t$(MAKE) generate-py"
          },
          {
            "lineNumber": 115,
            "text": "\t$(MAKE) generate-js"
          },
          {
            "lineNumber": 116,
            "text": "\t@echo"
          },
          {
            "lineNumber": 117,
            "text": "\t@echo 'File replacement done, commit with:'"
          },
          {
            "lineNumber": 118,
            "text": "\t@echo"
          },
          {
            "lineNumber": 119,
            "text": "\t@echo git commit -m '\"Bumped version to ${VERSION}\"' Makefile \\"
          },
          {
            "lineNumber": 120,
            "text": "\t\taddon/flamenco/__init__.py \\"
          },
          {
            "lineNumber": 121,
            "text": "\t\taddon/flamenco/manager \\"
          },
          {
            "lineNumber": 122,
            "text": "\t\taddon/flamenco/manager_README.md \\"
          },
          {
            "lineNumber": 123,
            "text": "\t\tmagefiles/version.go \\"
          },
          {
            "lineNumber": 124,
            "text": "\t\tweb/app/src/manager-api \\"
          },
          {
            "lineNumber": 125,
            "text": "\t\tweb/project-website/data/flamenco.yaml"
          },
          {
            "lineNumber": 126,
            "text": "\t@echo 'git tag -a -m \"Tagged version ${VERSION}\" v${VERSION}'"
          },
          {
            "lineNumber": 127
          },
          {
            "lineNumber": 128,
            "text": "version:"
          },
          {
            "lineNumber": 129,
            "text": "\t@echo \"Package     : ${PKG}\""
          },
          {
            "lineNumber": 130,
            "text": "\t@echo \"Version     : ${VERSION}\""
          },
          {
            "lineNumber": 131,
            "text": "\t@echo \"Git Hash    : ${GITHASH}\""
          },
          {
            "lineNumber": 132,
            "text": "\t@echo -n \"GOOS        : \"; go env GOOS"
          },
          {
            "lineNumber": 133,
            "text": "\t@echo -n \"GOARCH      : \"; go env GOARCH"
          },
          {
            "lineNumber": 134,
            "text": "\t@echo"
          },
          {
            "lineNumber": 135,
            "text": "\t@env | grep GO"
          },
          {
            "lineNumber": 136
          },
          {
            "lineNumber": 137,
            "text": "list-embedded:"
          },
          {
            "lineNumber": 138,
            "text": "\t@go list -f '{{printf \"%10s\" .Name}}: {{.EmbedFiles}}' ${PKG}/..."
          },
          {
            "lineNumber": 139
          },
          {
            "lineNumber": 140,
            "text": "swagger-ui:"
          },
          {
            "lineNumber": 141,
            "text": "\tgit clone --depth 1 https://github.com/swagger-api/swagger-ui.git tmp-swagger-ui"
          },
          {
            "lineNumber": 142,
            "text": "\trm -rf pkg/api/static/swagger-ui"
          },
          {
            "lineNumber": 143,
            "text": "\tmv tmp-swagger-ui/dist pkg/api/static/swagger-ui"
          },
          {
            "lineNumber": 144,
            "text": "\trm -rf tmp-swagger-ui"
          },
          {
            "lineNumber": 145,
            "text": "\t@echo"
          },
          {
            "lineNumber": 146,
            "text": "\t@echo 'Now update pkg/api/static/swagger-ui/index.html to have url: \"/api/openapi3.json\",'"
          },
          {
            "lineNumber": 147
          },
          {
            "lineNumber": 148,
            "text": "test: buildtool"
          },
          {
            "lineNumber": 149,
            "text": "\t\"${BUILDTOOL_PATH}\" test"
          },
          {
            "lineNumber": 150
          },
          {
            "lineNumber": 151,
            "text": "clean: buildtool"
          },
          {
            "lineNumber": 152,
            "text": "\t\"${BUILDTOOL_PATH}\" clean"
          },
          {
            "lineNumber": 153
          },
          {
            "lineNumber": 154,
            "text": "devserver-website:"
          },
          {
            "lineNumber": 155,
            "text": "\tgo run ${HUGO_PKG} -s web/project-website serve"
          },
          {
            "lineNumber": 156
          },
          {
            "lineNumber": 157,
            "text": "devserver-webapp: buildtool"
          },
          {
            "lineNumber": 158,
            "text": "\t\"${BUILDTOOL_PATH}\" devServerWebapp"
          },
          {
            "lineNumber": 159
          },
          {
            "lineNumber": 160,
            "text": "format: buildtool"
          },
          {
            "lineNumber": 161,
            "text": "\t\"${BUILDTOOL_PATH}\" format"
          },
          {
            "lineNumber": 162
          },
          {
            "lineNumber": 163,
            "text": "format-check: buildtool"
          },
          {
            "lineNumber": 164,
            "text": "\t\"${BUILDTOOL_PATH}\" formatCheck"
          },
          {
            "lineNumber": 165
          },
          {
            "lineNumber": 166,
            "text": "deploy-website:"
          },
          {
            "lineNumber": 167,
            "text": "\t$(MAKE) -s check-environment"
          }
        ],
        "range": {
          "endPosition": {
            "line": 167
          },
          "startPosition": {
            "line": 108
          }
        },
        "relativeWorkspacePath": ".\\Makefile",
        "signatures": {}
      },
      "score": 0.2558136582374573
    },
    {
      "codeBlock": {
        "contents": "{\n  \"tabWidth\": 2,\n  \"useTabs\": false,\n  \"singleQuote\": true,\n  \"semi\": true,\n  \"bracketSpacing\": true,\n  \"bracketSameLine\": true,\n  \"arrowParens\": \"always\"\n}",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "{"
          },
          {
            "lineNumber": 2,
            "text": "  \"tabWidth\": 2,"
          },
          {
            "lineNumber": 3,
            "text": "  \"useTabs\": false,"
          },
          {
            "lineNumber": 4,
            "text": "  \"singleQuote\": true,"
          },
          {
            "lineNumber": 5,
            "text": "  \"semi\": true,"
          },
          {
            "lineNumber": 6,
            "text": "  \"bracketSpacing\": true,"
          },
          {
            "lineNumber": 7,
            "text": "  \"bracketSameLine\": true,"
          },
          {
            "lineNumber": 8,
            "text": "  \"arrowParens\": \"always\""
          },
          {
            "lineNumber": 9,
            "text": "}"
          }
        ],
        "range": {
          "endPosition": {
            "line": 9
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\.prettierrc",
        "signatures": {}
      },
      "score": 0.2550854980945587
    },
    {
      "codeBlock": {
        "contents": "//go:build mage\n\npackage main\n\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nimport (\n\t\"context\"\n\n\t\"github.com/magefile/mage/mg\"\n\t\"github.com/magefile/mage/sh\"\n)\n\nvar (\n\tgenerators = []string{\n\t\t\"github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.9.0\",\n\t\t\"github.com/golang/mock/mockgen@v1.6.0\",\n\t}\n)\n\n// Install build-time dependencies: code generators and NodeJS dependencies.\nfunc InstallDeps() {\n\tmg.SerialDeps(InstallGenerators, InstallDepsWebapp)\n}\n\n// Install code generators.\nfunc InstallGenerators(ctx context.Context) error {\n\tr := NewRunner(ctx)\n\tfor _, pkg := range generators {\n\t\tr.Run(mg.GoCmd(), \"install\", pkg)\n\t}\n\treturn r.Wait()\n}\n\n// Use Yarn to install the webapp's NodeJS dependencies\nfunc InstallDepsWebapp() error {\n\tenv := map[string]string{\n\t\t\"MSYS2_ARG_CONV_EXCL\": \"*\",\n\t}\n\treturn sh.RunWithV(env,\n\t\t\"yarn\",\n\t\t\"--cwd\", \"web/app\",\n\t\t\"install\",\n\t)\n}",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "//go:build mage"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "package main"
          },
          {
            "lineNumber": 4
          },
          {
            "lineNumber": 5,
            "text": "// SPDX-License-Identifier: GPL-3.0-or-later"
          },
          {
            "lineNumber": 6
          },
          {
            "lineNumber": 7,
            "text": "import ("
          },
          {
            "lineNumber": 8,
            "text": "\t\"context\""
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "\t\"github.com/magefile/mage/mg\""
          },
          {
            "lineNumber": 11,
            "text": "\t\"github.com/magefile/mage/sh\""
          },
          {
            "lineNumber": 12,
            "text": ")"
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "var ("
          },
          {
            "lineNumber": 15,
            "text": "\tgenerators = []string{"
          },
          {
            "lineNumber": 16,
            "text": "\t\t\"github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.9.0\","
          },
          {
            "lineNumber": 17,
            "text": "\t\t\"github.com/golang/mock/mockgen@v1.6.0\","
          },
          {
            "lineNumber": 18,
            "text": "\t}"
          },
          {
            "lineNumber": 19,
            "text": ")"
          },
          {
            "lineNumber": 20
          },
          {
            "lineNumber": 21,
            "text": "// Install build-time dependencies: code generators and NodeJS dependencies."
          },
          {
            "lineNumber": 22,
            "text": "func InstallDeps() {"
          },
          {
            "lineNumber": 23,
            "text": "\tmg.SerialDeps(InstallGenerators, InstallDepsWebapp)"
          },
          {
            "lineNumber": 24,
            "text": "}"
          },
          {
            "lineNumber": 25
          },
          {
            "lineNumber": 26,
            "text": "// Install code generators."
          },
          {
            "lineNumber": 27,
            "text": "func InstallGenerators(ctx context.Context) error {"
          },
          {
            "lineNumber": 28,
            "text": "\tr := NewRunner(ctx)"
          },
          {
            "lineNumber": 29,
            "text": "\tfor _, pkg := range generators {"
          },
          {
            "lineNumber": 30,
            "text": "\t\tr.Run(mg.GoCmd(), \"install\", pkg)"
          },
          {
            "lineNumber": 31,
            "text": "\t}"
          },
          {
            "lineNumber": 32,
            "text": "\treturn r.Wait()"
          },
          {
            "lineNumber": 33,
            "text": "}"
          },
          {
            "lineNumber": 34
          },
          {
            "lineNumber": 35,
            "text": "// Use Yarn to install the webapp's NodeJS dependencies"
          },
          {
            "lineNumber": 36,
            "text": "func InstallDepsWebapp() error {"
          },
          {
            "lineNumber": 37,
            "text": "\tenv := map[string]string{"
          },
          {
            "lineNumber": 38,
            "text": "\t\t\"MSYS2_ARG_CONV_EXCL\": \"*\","
          },
          {
            "lineNumber": 39,
            "text": "\t}"
          },
          {
            "lineNumber": 40,
            "text": "\treturn sh.RunWithV(env,"
          },
          {
            "lineNumber": 41,
            "text": "\t\t\"yarn\","
          },
          {
            "lineNumber": 42,
            "text": "\t\t\"--cwd\", \"web/app\","
          },
          {
            "lineNumber": 43,
            "text": "\t\t\"install\","
          },
          {
            "lineNumber": 44,
            "text": "\t)"
          },
          {
            "lineNumber": 45,
            "text": "}"
          }
        ],
        "range": {
          "endPosition": {
            "column": 1,
            "line": 44
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\magefiles\\install.go",
        "signatures": {}
      },
      "score": 0.2525862455368042
    },
    {
      "codeBlock": {
        "contents": "[tool.poetry]\nname = \"flamenco\"\nversion = \"3.0\"\ndescription = \"Flamenco client add-on for Blender\"\nauthors = [\"Sybren A. Stüvel \u003csybren@blender.org\u003e\"]\nlicense = \"GPL-3.0-or-later\"\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\nrequests = \"^2.27.1\"\nopenapi-core = \"^0.14.2\"\npython-dateutil = \"^2.8.2\"\n\n[tool.poetry.dev-dependencies]\nmypy = \"^0.941\"\nblack = \"^22.1.0\"\n\n[build-system]\nrequires = [\"poetry\u003e=0.12\"]\nbuild-backend = \"poetry.masonry.api\"",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "[tool.poetry]"
          },
          {
            "lineNumber": 2,
            "text": "name = \"flamenco\""
          },
          {
            "lineNumber": 3,
            "text": "version = \"3.0\""
          },
          {
            "lineNumber": 4,
            "text": "description = \"Flamenco client add-on for Blender\""
          },
          {
            "lineNumber": 5,
            "text": "authors = [\"Sybren A. Stüvel \u003csybren@blender.org\u003e\"]"
          },
          {
            "lineNumber": 6,
            "text": "license = \"GPL-3.0-or-later\""
          },
          {
            "lineNumber": 7
          },
          {
            "lineNumber": 8,
            "text": "[tool.poetry.dependencies]"
          },
          {
            "lineNumber": 9,
            "text": "python = \"^3.10\""
          },
          {
            "lineNumber": 10,
            "text": "requests = \"^2.27.1\""
          },
          {
            "lineNumber": 11,
            "text": "openapi-core = \"^0.14.2\""
          },
          {
            "lineNumber": 12,
            "text": "python-dateutil = \"^2.8.2\""
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "[tool.poetry.dev-dependencies]"
          },
          {
            "lineNumber": 15,
            "text": "mypy = \"^0.941\""
          },
          {
            "lineNumber": 16,
            "text": "black = \"^22.1.0\""
          },
          {
            "lineNumber": 17
          },
          {
            "lineNumber": 18,
            "text": "[build-system]"
          },
          {
            "lineNumber": 19,
            "text": "requires = [\"poetry\u003e=0.12\"]"
          },
          {
            "lineNumber": 20,
            "text": "build-backend = \"poetry.masonry.api\""
          }
        ],
        "range": {
          "endPosition": {
            "line": 20
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\addon\\pyproject.toml",
        "signatures": {}
      },
      "score": 0.25147581100463867
    },
    {
      "codeBlock": {
        "contents": "# Flamenco\n\nThis repository contains the sources for Flamenco. The Manager, Worker, and\nBlender add-on sources are all combined in this one repository.\n\nThe documentation is available on https://flamenco.blender.org/, including\ninstructions on how to set up a development environment \u0026 build Flamenco for the\nfirst time.\n\nTo access the documentation offline, go to the &#96;web/project-website/content&#96;\ndirectory here in the source files.\n\n\n## License\n\nFlamenco is licensed under the GPLv3+ license.",
        "detailedLines": [
          {
            "lineNumber": 1,
            "text": "# Flamenco"
          },
          {
            "lineNumber": 2
          },
          {
            "lineNumber": 3,
            "text": "This repository contains the sources for Flamenco. The Manager, Worker, and"
          },
          {
            "lineNumber": 4,
            "text": "Blender add-on sources are all combined in this one repository."
          },
          {
            "lineNumber": 5
          },
          {
            "lineNumber": 6,
            "text": "The documentation is available on https://flamenco.blender.org/, including"
          },
          {
            "lineNumber": 7,
            "text": "instructions on how to set up a development environment \u0026 build Flamenco for the"
          },
          {
            "lineNumber": 8,
            "text": "first time."
          },
          {
            "lineNumber": 9
          },
          {
            "lineNumber": 10,
            "text": "To access the documentation offline, go to the &#96;web/project-website/content&#96;"
          },
          {
            "lineNumber": 11,
            "text": "directory here in the source files."
          },
          {
            "lineNumber": 12
          },
          {
            "lineNumber": 13
          },
          {
            "lineNumber": 14,
            "text": "## License"
          },
          {
            "lineNumber": 15
          },
          {
            "lineNumber": 16,
            "text": "Flamenco is licensed under the GPLv3+ license."
          }
        ],
        "range": {
          "endPosition": {
            "line": 16
          },
          "startPosition": {}
        },
        "relativeWorkspacePath": ".\\README.md",
        "signatures": {}
      },
      "score": 0.24031126499176025
    }
  ]
}
```
Status: **completed**


</details>
</tool-use>

<tool-use data-tool-type="unknown" data-tool-name="semantic_search_full"><details>
<summary>Tool use: **semantic_search_full**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="search" data-tool-name="glob_file_search">
<details>
<summary>Tool use: **glob_file_search** • Searched codebase "" • **0** results in **1** directory</summary>

Directory: **** (0 files)

</details>
</tool-use>

<tool-use data-tool-type="search" data-tool-name="glob_file_search"><details>
<summary>Tool use: **glob_file_search**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="search" data-tool-name="glob_file_search">
<details>
<summary>Tool use: **glob_file_search** • Searched codebase "" • **1** result in **1** directory</summary>

Directory: **** (1 file)

| File |
|------|
| `.editorconfig` |

</details>
</tool-use>

<tool-use data-tool-type="search" data-tool-name="glob_file_search"><details>
<summary>Tool use: **glob_file_search**</summary>

</details></tool-use>

_**Agent (default)**_

Checking `.gitignore` and project structure:


_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\.gitignore</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

<tool-use data-tool-type="read" data-tool-name="read_file_v2">
<details><summary>Tool use: **read_file_v2** • Read file: e:\flamenco\.editorconfig</summary>

</details>
</tool-use>

<tool-use data-tool-type="read" data-tool-name="read_file_v2"><details>
<summary>Tool use: **read_file_v2**</summary>

</details></tool-use>

_**Agent (default)**_

Based on the codebase:

**Sybren likely uses VSCode**, but probably not the Blender Development extension:

1. `.vscode/extensions.json` exists and recommends Go, SQLite, Prettier, and Vue extensions — but not the Blender Development extension.
2. `.vscode/settings.json` and `.vscode/launch.json` are gitignored (personal settings).
3. The `addon/README.md` shows a command-line workflow (venv, poetry, `make generate-py`) with no IDE-specific steps.
4. There's an `.editorconfig` (editor-agnostic).

**Likely workflow:**
- Edit Python files in VSCode
- Test by reloading the addon in Blender manually
- Use command-line tools (poetry, make) for dependencies and code generation

The Blender Development extension is optional. You can use it for auto-reload and debugging, or stick with manual reloading. The extension should work with your `addon\flamenco\` structure if you want to use it.
