|
139 | 139 | class ScriptLanguage(Enum):
|
140 | 140 | BASH = 'bash'
|
141 | 141 | PWSH = 'PowerShell'
|
| 142 | + PYTHON = 'python' |
142 | 143 |
|
143 | 144 |
|
144 | 145 | class IncludeResolver:
|
@@ -185,7 +186,7 @@ def _includeLiteral(self, content):
|
185 | 186 | raise NotImplementedError()
|
186 | 187 |
|
187 | 188 | def _resolveContent(self, result):
|
188 |
| - raise NotImplementedError() |
| 189 | + return result |
189 | 190 |
|
190 | 191 | def resolve(self, result):
|
191 | 192 | return (self._resolveContent(result), "\n".join(self.__incDigests), self.__incFiles)
|
@@ -397,20 +398,12 @@ def setupFingerprint(spec, env):
|
397 | 398 |
|
398 | 399 |
|
399 | 400 | class PwshResolver(IncludeResolver):
|
400 |
| - def __init__(self, fileLoader, baseDir, origText, sourceName, varBase): |
401 |
| - super().__init__(fileLoader, baseDir, origText, sourceName, varBase) |
402 |
| - self.prolog = [] |
403 |
| - self.count = 0 |
404 |
| - |
405 | 401 | def _includeFile(self, name):
|
406 | 402 | return '"$_BOB_TMP_BASE/' + escapePwsh(name) + '"'
|
407 | 403 |
|
408 | 404 | def _includeLiteral(self, content):
|
409 | 405 | return quotePwsh(content.decode('utf8'))
|
410 | 406 |
|
411 |
| - def _resolveContent(self, result): |
412 |
| - return "\n".join(self.prolog + [result]) |
413 |
| - |
414 | 407 |
|
415 | 408 | class PwshLanguage:
|
416 | 409 | index = ScriptLanguage.PWSH
|
@@ -574,9 +567,150 @@ def setupFingerprint(spec, env):
|
574 | 567 | return [interpreter, "-c", spec.fingerprintScript]
|
575 | 568 |
|
576 | 569 |
|
| 570 | +class PythonResolver(IncludeResolver): |
| 571 | + def _includeFile(self, name): |
| 572 | + return 'os.path.join(_BOB_TMP_BASE, ' + repr(name) + ')' |
| 573 | + |
| 574 | + def _includeLiteral(self, content): |
| 575 | + return repr(content.decode('utf8')) |
| 576 | + |
| 577 | + |
| 578 | +class PythonLanguage: |
| 579 | + index = ScriptLanguage.PYTHON |
| 580 | + glue = "\nos.chdir(os.environ['BOB_CWD'])\n" |
| 581 | + Resolver = PythonResolver |
| 582 | + |
| 583 | + HELPERS = dedent("""\ |
| 584 | + from subprocess import run, call, check_call, check_output |
| 585 | + import os, os.path, sys |
| 586 | + """) |
| 587 | + |
| 588 | + @staticmethod |
| 589 | + def __formatProlog(spec, tmpDir): |
| 590 | + pathSep = ";" if sys.platform == "win32" else ":" |
| 591 | + env = { key : repr(value) for (key, value) in spec.env.items() } |
| 592 | + env.update({ |
| 593 | + "PATH": '"' + pathSep + '".join([' + ", ".join( |
| 594 | + [repr(os.path.abspath(p)) for p in spec.paths] + |
| 595 | + (['os.environ["PATH"]'] if not spec.hasSandbox else |
| 596 | + [repr(p) for p in spec.sandboxPaths]) |
| 597 | + ) + '])', |
| 598 | + "LD_LIBRARY_PATH": '"' + pathSep + '".join(' + |
| 599 | + repr([ os.path.abspath(p) for p in spec.libraryPaths ]) + ')', |
| 600 | + "BOB_CWD": repr(os.path.abspath(spec.workspaceExecPath)), |
| 601 | + }) |
| 602 | + |
| 603 | + ret = [ |
| 604 | + "# Convenience helpers", |
| 605 | + PythonLanguage.HELPERS, |
| 606 | + "", |
| 607 | + "# Special Bob array variables:", |
| 608 | + "BOB_ALL_PATHS = dict({})".format(repr(sorted( |
| 609 | + [ (name, os.path.abspath(path)) for name, path in spec.allPaths ] |
| 610 | + ))), |
| 611 | + "BOB_DEP_PATHS = dict({})".format(repr(sorted( |
| 612 | + [ (name, os.path.abspath(path)) for name,path in spec.depPaths ] |
| 613 | + ))), |
| 614 | + "BOB_TOOL_PATHS = dict({})".format(repr(sorted( |
| 615 | + [ (name, os.path.abspath(path)) for name,path in spec.toolPaths ] |
| 616 | + ))), |
| 617 | + '_BOB_TMP_BASE = ' + repr("/tmp" if spec.hasSandbox else os.path.join(tmpDir, "tmp")), |
| 618 | + "", |
| 619 | + "# Environment:", |
| 620 | + "\n".join('os.environ["{}"] = {}'.format(k, v) for (k,v) in sorted(env.items())), |
| 621 | + ] |
| 622 | + return "\n".join(ret) |
| 623 | + |
| 624 | + @staticmethod |
| 625 | + def __formatSetup(spec): |
| 626 | + return "\n".join([ |
| 627 | + "", |
| 628 | + "# Recipe setup script", |
| 629 | + spec.setupScript, |
| 630 | + "os.chdir(os.environ['BOB_CWD'])", |
| 631 | + ]) |
| 632 | + |
| 633 | + @staticmethod |
| 634 | + def __formatScript(spec, tmpDir, trace): |
| 635 | + if spec.envFile: |
| 636 | + envFile = "/bob/env" if spec.hasSandbox else os.path.abspath(spec.envFile) |
| 637 | + else: |
| 638 | + envFile = None |
| 639 | + ret = [ |
| 640 | + PythonLanguage.__formatProlog(spec, tmpDir), |
| 641 | + "", |
| 642 | + "# Setup", |
| 643 | + dedent("""\ |
| 644 | + with open({ENV_FILE}, "w") as f: |
| 645 | + f.write(repr({{ "env" : dict(os.environ), "globals" : globals() }})) |
| 646 | + """.format(ENV_FILE=repr(envFile))) if envFile else "", |
| 647 | + "os.chdir(os.environ['BOB_CWD'])", |
| 648 | + "", |
| 649 | + PythonLanguage.__formatSetup(spec), |
| 650 | + "", |
| 651 | + "# Recipe main script", |
| 652 | + spec.mainScript, |
| 653 | + ] |
| 654 | + return "\n".join(ret) |
| 655 | + |
| 656 | + @staticmethod |
| 657 | + def __scriptFilePaths(spec, tmpDir): |
| 658 | + if spec.hasSandbox: |
| 659 | + execScriptFile = "/.script.py" |
| 660 | + realScriptFile = (spec.scriptHint or os.path.join(tmpDir, ".script")) + ".py" |
| 661 | + else: |
| 662 | + execScriptFile = (spec.scriptHint or os.path.join(tmpDir, "script")) + ".py" |
| 663 | + realScriptFile = execScriptFile |
| 664 | + return (os.path.abspath(realScriptFile), os.path.abspath(execScriptFile)) |
| 665 | + |
| 666 | + @staticmethod |
| 667 | + def setupShell(spec, tmpDir, keepEnv): |
| 668 | + realScriptFile, execScriptFile = PythonLanguage.__scriptFilePaths(spec, tmpDir) |
| 669 | + with open(realScriptFile, "w") as f: |
| 670 | + f.write(PythonLanguage.__formatProlog(spec, tmpDir)) |
| 671 | + f.write(PythonLanguage.__formatSetup(spec)) |
| 672 | + |
| 673 | + args = [sys.executable, "-i", execScriptFile] |
| 674 | + args.extend(os.path.abspath(a) for a in spec.args) |
| 675 | + |
| 676 | + return (realScriptFile, execScriptFile, args) |
| 677 | + |
| 678 | + @staticmethod |
| 679 | + def setupCall(spec, tmpDir, keepEnv, trace): |
| 680 | + realScriptFile, execScriptFile = PythonLanguage.__scriptFilePaths(spec, tmpDir) |
| 681 | + with open(realScriptFile, "w") as f: |
| 682 | + f.write(PythonLanguage.__formatScript(spec, tmpDir, trace)) |
| 683 | + |
| 684 | + args = [sys.executable, execScriptFile] |
| 685 | + args.extend(os.path.abspath(a) for a in spec.args) |
| 686 | + |
| 687 | + return (realScriptFile, execScriptFile, args) |
| 688 | + |
| 689 | + @staticmethod |
| 690 | + def mangleFingerprints(scriptFragments, env): |
| 691 | + # join the script fragments first |
| 692 | + script = joinScripts(scriptFragments, PythonLanguage.glue) |
| 693 | + |
| 694 | + # do not add preamble for empty scripts |
| 695 | + if not script: return "" |
| 696 | + |
| 697 | + # Add snippets as they match and a default settings preamble |
| 698 | + ret = [script] |
| 699 | + for n,v in sorted(env.items()): |
| 700 | + ret.append('os.environ["{}"] = {}'.format(k, repr(v))) |
| 701 | + ret.append(PythonLanguage.HELPERS) |
| 702 | + |
| 703 | + return "\n".join(reversed(ret)) |
| 704 | + |
| 705 | + @staticmethod |
| 706 | + def setupFingerprint(spec, env): |
| 707 | + return [sys.executable, "-c", spec.fingerprintScript] |
| 708 | + |
| 709 | + |
577 | 710 | LANG = {
|
578 | 711 | ScriptLanguage.BASH : BashLanguage,
|
579 | 712 | ScriptLanguage.PWSH : PwshLanguage,
|
| 713 | + ScriptLanguage.PYTHON : PythonLanguage, |
580 | 714 | }
|
581 | 715 |
|
582 | 716 | def getLanguage(language):
|
|
0 commit comments