rbx.cp 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. rbx/__init__.py +0 -0
  2. rbx/annotations.py +127 -0
  3. rbx/autoenum.py +333 -0
  4. rbx/box/__init__.py +0 -0
  5. rbx/box/builder.py +77 -0
  6. rbx/box/cd.py +37 -0
  7. rbx/box/checkers.py +134 -0
  8. rbx/box/code.py +185 -0
  9. rbx/box/compile.py +56 -0
  10. rbx/box/conftest.py +42 -0
  11. rbx/box/contest/__init__.py +0 -0
  12. rbx/box/contest/build_contest_statements.py +347 -0
  13. rbx/box/contest/contest_package.py +76 -0
  14. rbx/box/contest/contest_utils.py +20 -0
  15. rbx/box/contest/main.py +179 -0
  16. rbx/box/contest/schema.py +155 -0
  17. rbx/box/contest/statements.py +82 -0
  18. rbx/box/creation.py +72 -0
  19. rbx/box/download.py +64 -0
  20. rbx/box/environment.py +345 -0
  21. rbx/box/extensions.py +26 -0
  22. rbx/box/generators.py +478 -0
  23. rbx/box/generators_test.py +63 -0
  24. rbx/box/main.py +449 -0
  25. rbx/box/package.py +316 -0
  26. rbx/box/packaging/boca/extension.py +27 -0
  27. rbx/box/packaging/boca/packager.py +245 -0
  28. rbx/box/packaging/contest_main.py +82 -0
  29. rbx/box/packaging/main.py +68 -0
  30. rbx/box/packaging/packager.py +117 -0
  31. rbx/box/packaging/polygon/packager.py +320 -0
  32. rbx/box/packaging/polygon/test.py +81 -0
  33. rbx/box/packaging/polygon/xml_schema.py +106 -0
  34. rbx/box/presets/__init__.py +503 -0
  35. rbx/box/presets/fetch.py +70 -0
  36. rbx/box/presets/lock_schema.py +20 -0
  37. rbx/box/presets/schema.py +59 -0
  38. rbx/box/schema.py +394 -0
  39. rbx/box/solutions.py +792 -0
  40. rbx/box/solutions_test.py +41 -0
  41. rbx/box/statements/__init__.py +0 -0
  42. rbx/box/statements/build_statements.py +359 -0
  43. rbx/box/statements/builders.py +375 -0
  44. rbx/box/statements/joiners.py +113 -0
  45. rbx/box/statements/latex.py +47 -0
  46. rbx/box/statements/latex_jinja.py +214 -0
  47. rbx/box/statements/schema.py +138 -0
  48. rbx/box/stresses.py +292 -0
  49. rbx/box/stressing/__init__.py +0 -0
  50. rbx/box/stressing/finder_parser.py +359 -0
  51. rbx/box/stressing/generator_parser.py +258 -0
  52. rbx/box/testcases.py +54 -0
  53. rbx/box/ui/__init__.py +0 -0
  54. rbx/box/ui/captured_log.py +372 -0
  55. rbx/box/ui/css/app.tcss +48 -0
  56. rbx/box/ui/main.py +38 -0
  57. rbx/box/ui/run.py +209 -0
  58. rbx/box/validators.py +245 -0
  59. rbx/box/validators_test.py +15 -0
  60. rbx/checker.py +128 -0
  61. rbx/clone.py +197 -0
  62. rbx/config.py +271 -0
  63. rbx/conftest.py +38 -0
  64. rbx/console.py +27 -0
  65. rbx/create.py +37 -0
  66. rbx/edit.py +24 -0
  67. rbx/grading/__init__.py +0 -0
  68. rbx/grading/caching.py +356 -0
  69. rbx/grading/conftest.py +33 -0
  70. rbx/grading/judge/__init__.py +0 -0
  71. rbx/grading/judge/cacher.py +503 -0
  72. rbx/grading/judge/digester.py +35 -0
  73. rbx/grading/judge/sandbox.py +748 -0
  74. rbx/grading/judge/sandboxes/__init__.py +0 -0
  75. rbx/grading/judge/sandboxes/isolate.py +683 -0
  76. rbx/grading/judge/sandboxes/stupid_sandbox.py +310 -0
  77. rbx/grading/judge/sandboxes/timeit.py +217 -0
  78. rbx/grading/judge/storage.py +284 -0
  79. rbx/grading/judge/test.py +38 -0
  80. rbx/grading/judge/testiso.py +54 -0
  81. rbx/grading/steps.py +522 -0
  82. rbx/grading/steps_with_caching.py +59 -0
  83. rbx/grading/steps_with_caching_run_test.py +429 -0
  84. rbx/grading_utils.py +148 -0
  85. rbx/hydration.py +101 -0
  86. rbx/main.py +122 -0
  87. rbx/metadata.py +105 -0
  88. rbx/providers/__init__.py +43 -0
  89. rbx/providers/codeforces.py +73 -0
  90. rbx/providers/provider.py +26 -0
  91. rbx/resources/checkers/boilerplate.cpp +20 -0
  92. rbx/resources/default_config.json +48 -0
  93. rbx/resources/envs/default.rbx.yml +37 -0
  94. rbx/resources/envs/isolate.rbx.yml +37 -0
  95. rbx/resources/packagers/boca/checker.sh +43 -0
  96. rbx/resources/packagers/boca/compare +53 -0
  97. rbx/resources/packagers/boca/compile/c +172 -0
  98. rbx/resources/packagers/boca/compile/cc +173 -0
  99. rbx/resources/packagers/boca/compile/cpp +172 -0
  100. rbx/resources/packagers/boca/compile/java +194 -0
  101. rbx/resources/packagers/boca/compile/kt +155 -0
  102. rbx/resources/packagers/boca/compile/pas +172 -0
  103. rbx/resources/packagers/boca/compile/py2 +173 -0
  104. rbx/resources/packagers/boca/compile/py3 +173 -0
  105. rbx/resources/packagers/boca/run/c +128 -0
  106. rbx/resources/packagers/boca/run/cc +128 -0
  107. rbx/resources/packagers/boca/run/cpp +128 -0
  108. rbx/resources/packagers/boca/run/java +194 -0
  109. rbx/resources/packagers/boca/run/kt +159 -0
  110. rbx/resources/packagers/boca/run/py2 +166 -0
  111. rbx/resources/packagers/boca/run/py3 +166 -0
  112. rbx/resources/presets/default/contest/contest.rbx.yml +14 -0
  113. rbx/resources/presets/default/contest/statement/contest.rbx.tex +97 -0
  114. rbx/resources/presets/default/contest/statement/olymp.sty +250 -0
  115. rbx/resources/presets/default/contest/statement/template.rbx.tex +42 -0
  116. rbx/resources/presets/default/preset.rbx.yml +12 -0
  117. rbx/resources/presets/default/problem/.gitignore +6 -0
  118. rbx/resources/presets/default/problem/gen.cpp +9 -0
  119. rbx/resources/presets/default/problem/problem.rbx.yml +44 -0
  120. rbx/resources/presets/default/problem/random.py +3 -0
  121. rbx/resources/presets/default/problem/random.txt +2 -0
  122. rbx/resources/presets/default/problem/sols/main.cpp +9 -0
  123. rbx/resources/presets/default/problem/sols/slow.cpp +15 -0
  124. rbx/resources/presets/default/problem/sols/wa.cpp +9 -0
  125. rbx/resources/presets/default/problem/statement/olymp.sty +250 -0
  126. rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  127. rbx/resources/presets/default/problem/statement/statement.rbx.tex +18 -0
  128. rbx/resources/presets/default/problem/statement/template.rbx.tex +89 -0
  129. rbx/resources/presets/default/problem/tests/samples/000.in +1 -0
  130. rbx/resources/presets/default/problem/tests/samples/001.in +1 -0
  131. rbx/resources/presets/default/problem/validator.cpp +16 -0
  132. rbx/resources/presets/default/problem/wcmp.cpp +34 -0
  133. rbx/resources/templates/template.cpp +19 -0
  134. rbx/run.py +45 -0
  135. rbx/schema.py +64 -0
  136. rbx/submit.py +61 -0
  137. rbx/submitors/__init__.py +18 -0
  138. rbx/submitors/codeforces.py +120 -0
  139. rbx/submitors/submitor.py +25 -0
  140. rbx/test.py +347 -0
  141. rbx/testcase.py +70 -0
  142. rbx/testcase_rendering.py +79 -0
  143. rbx/testdata/box1/gen1.cpp +7 -0
  144. rbx/testdata/box1/gen2.cpp +9 -0
  145. rbx/testdata/box1/genScript.py +2 -0
  146. rbx/testdata/box1/hard-tle.sol.cpp +26 -0
  147. rbx/testdata/box1/ole.cpp +17 -0
  148. rbx/testdata/box1/problem.rbx.yml +39 -0
  149. rbx/testdata/box1/re.sol.cpp +23 -0
  150. rbx/testdata/box1/sol.cpp +22 -0
  151. rbx/testdata/box1/tests/1.in +1 -0
  152. rbx/testdata/box1/tle-and-incorrect.sol.cpp +33 -0
  153. rbx/testdata/box1/tle.sol.cpp +35 -0
  154. rbx/testdata/box1/validator.cpp +11 -0
  155. rbx/testdata/box1/wa.sol.cpp +22 -0
  156. rbx/testdata/caching/executable.py +1 -0
  157. rbx/testdata/compatible +0 -0
  158. rbx/testing_utils.py +65 -0
  159. rbx/utils.py +162 -0
  160. rbx_cp-0.5.0.dist-info/LICENSE +201 -0
  161. rbx_cp-0.5.0.dist-info/METADATA +89 -0
  162. rbx_cp-0.5.0.dist-info/RECORD +164 -0
  163. rbx_cp-0.5.0.dist-info/WHEEL +4 -0
  164. rbx_cp-0.5.0.dist-info/entry_points.txt +4 -0
rbx/box/checkers.py ADDED
@@ -0,0 +1,134 @@
1
+ import pathlib
2
+ from typing import Optional
3
+
4
+ import typer
5
+
6
+ from rbx import console
7
+ from rbx.box import package
8
+ from rbx.box.code import compile_item, run_item
9
+ from rbx.box.schema import Testcase
10
+ from rbx.grading.judge.sandbox import SandboxBase
11
+ from rbx.grading.steps import (
12
+ CheckerResult,
13
+ DigestHolder,
14
+ DigestOrDest,
15
+ DigestOrSource,
16
+ GradingFileInput,
17
+ Outcome,
18
+ RunLog,
19
+ )
20
+
21
+
22
+ def compile_checker() -> str:
23
+ checker = package.get_checker()
24
+
25
+ try:
26
+ digest = compile_item(checker)
27
+ except Exception as e:
28
+ console.console.print('[error]Failed compiling checker.[/error]')
29
+ raise typer.Exit(1) from e
30
+ return digest
31
+
32
+
33
+ def _check_pre_output(run_log: Optional[RunLog]) -> CheckerResult:
34
+ pkg = package.find_problem_package_or_die()
35
+
36
+ if run_log is None:
37
+ return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
38
+
39
+ timelimit = pkg.timelimit_for_language(run_log.get_run_language())
40
+ if run_log.time is not None and run_log.time * 1000 > timelimit * 2:
41
+ return CheckerResult(outcome=Outcome.TIME_LIMIT_EXCEEDED)
42
+
43
+ if run_log.exitstatus in [SandboxBase.EXIT_SIGNAL, SandboxBase.EXIT_NONZERO_RETURN]:
44
+ return CheckerResult(outcome=Outcome.RUNTIME_ERROR)
45
+ if run_log.exitstatus in [SandboxBase.EXIT_TIMEOUT, SandboxBase.EXIT_TIMEOUT_WALL]:
46
+ return CheckerResult(outcome=Outcome.TIME_LIMIT_EXCEEDED)
47
+ if run_log.exitstatus == SandboxBase.EXIT_MEMORY_LIMIT_EXCEEDED:
48
+ return CheckerResult(outcome=Outcome.MEMORY_LIMIT_EXCEEDED)
49
+ if run_log.exitstatus == SandboxBase.EXIT_SANDBOX_ERROR:
50
+ return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
51
+ if run_log.exitstatus == SandboxBase.EXIT_OUTPUT_LIMIT_EXCEEDED:
52
+ return CheckerResult(outcome=Outcome.OUTPUT_LIMIT_EXCEEDED)
53
+ return CheckerResult(outcome=Outcome.ACCEPTED)
54
+
55
+
56
+ def _convert_tle(result: CheckerResult, run_log: Optional[RunLog]) -> CheckerResult:
57
+ if result.outcome == Outcome.TIME_LIMIT_EXCEEDED:
58
+ # This already is a TLE outcome.
59
+ return result
60
+ pkg = package.find_problem_package_or_die()
61
+ if (
62
+ run_log is not None
63
+ and run_log.time is not None
64
+ and run_log.time * 1000
65
+ >= pkg.timelimit_for_language(run_log.get_run_language())
66
+ ):
67
+ # Soft TLE.
68
+ result.no_tle_outcome = result.outcome
69
+ result.outcome = Outcome.TIME_LIMIT_EXCEEDED
70
+ return result
71
+
72
+
73
+ def check_with_no_output(run_log: Optional[RunLog]) -> CheckerResult:
74
+ result = _check_pre_output(run_log)
75
+ return _convert_tle(result, run_log)
76
+
77
+
78
+ def check(
79
+ checker_digest: str,
80
+ run_log: Optional[RunLog],
81
+ testcase: Testcase,
82
+ program_output: pathlib.Path,
83
+ skip_run_log: bool = False,
84
+ ) -> CheckerResult:
85
+ if not skip_run_log:
86
+ result = _check_pre_output(run_log)
87
+ if result.outcome != Outcome.ACCEPTED:
88
+ return _convert_tle(result, run_log)
89
+
90
+ pkg = package.find_problem_package_or_die()
91
+ output_size = program_output.stat().st_size
92
+ if output_size > pkg.outputLimit * 1024:
93
+ return CheckerResult(
94
+ outcome=Outcome.OUTPUT_LIMIT_EXCEEDED,
95
+ message=f'Output size {pkg.outputLimit}kb, limit is {output_size // 1024}kb.',
96
+ )
97
+
98
+ error = DigestHolder()
99
+ inputs = [
100
+ GradingFileInput(
101
+ src=testcase.inputPath,
102
+ dest=pathlib.PosixPath('input.txt'),
103
+ ),
104
+ GradingFileInput(
105
+ src=testcase.outputPath,
106
+ dest=pathlib.PosixPath('expected.txt'),
107
+ ),
108
+ GradingFileInput(
109
+ src=program_output,
110
+ dest=pathlib.PosixPath('output.txt'),
111
+ ),
112
+ ]
113
+ checker_run_log = run_item(
114
+ package.get_checker(),
115
+ DigestOrSource.create(checker_digest),
116
+ stderr=DigestOrDest.create(error),
117
+ inputs=inputs,
118
+ extra_args='input.txt output.txt expected.txt',
119
+ )
120
+ message = package.get_digest_as_string(error.value or '') or ''
121
+
122
+ if checker_run_log is None or checker_run_log.exitcode not in [0, 1, 2, 3]:
123
+ return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
124
+
125
+ result = CheckerResult(outcome=Outcome.ACCEPTED, message=message)
126
+
127
+ if checker_run_log.exitcode in [1, 2]:
128
+ result = CheckerResult(outcome=Outcome.WRONG_ANSWER, message=message)
129
+ if checker_run_log.exitcode == 3:
130
+ result = CheckerResult(outcome=Outcome.JUDGE_FAILED, message=message)
131
+
132
+ if skip_run_log:
133
+ return result
134
+ return _convert_tle(result, run_log)
rbx/box/code.py ADDED
@@ -0,0 +1,185 @@
1
+ import pathlib
2
+ import shlex
3
+ import sys
4
+ from pathlib import PosixPath
5
+ from typing import List, Optional
6
+
7
+ import typer
8
+
9
+ from rbx.box import download, package
10
+ from rbx.box.environment import (
11
+ ExecutionConfig,
12
+ get_compilation_config,
13
+ get_execution_config,
14
+ get_extension_or_default,
15
+ get_file_mapping,
16
+ get_language,
17
+ get_mapped_command,
18
+ get_mapped_commands,
19
+ get_sandbox_params_from_config,
20
+ merge_execution_configs,
21
+ )
22
+ from rbx.box.extensions import MacExtension
23
+ from rbx.box.schema import CodeItem
24
+ from rbx.grading import steps_with_caching
25
+ from rbx.grading.steps import (
26
+ DigestHolder,
27
+ DigestOrDest,
28
+ DigestOrSource,
29
+ GradingArtifacts,
30
+ GradingFileInput,
31
+ GradingFileOutput,
32
+ RunLog,
33
+ RunLogMetadata,
34
+ )
35
+
36
+
37
+ def normalize_for_macos(commands: List[str]) -> List[str]:
38
+ def normalize(command: str) -> str:
39
+ extension = get_extension_or_default('mac', MacExtension)
40
+ if extension.gpp_alternative is None:
41
+ return command
42
+ return command.replace('g++', extension.gpp_alternative)
43
+
44
+ return [normalize(command) for command in commands]
45
+
46
+
47
+ def get_extension(code: CodeItem) -> str:
48
+ path: pathlib.Path = PosixPath(code.path)
49
+ return path.suffix[1:]
50
+
51
+
52
+ def find_language_name(code: CodeItem) -> str:
53
+ if code.language is not None:
54
+ return get_language(code.language).name
55
+ return get_language(get_extension(code)).name
56
+
57
+
58
+ # Compile code item and return its digest in the storage.
59
+ def compile_item(code: CodeItem) -> str:
60
+ generator_path = PosixPath(code.path)
61
+ language = find_language_name(code)
62
+ compilation_options = get_compilation_config(language)
63
+ file_mapping = get_file_mapping(language)
64
+ dependency_cache = package.get_dependency_cache()
65
+ sandbox = package.get_singleton_sandbox()
66
+ sandbox_params = get_sandbox_params_from_config(compilation_options.sandbox)
67
+
68
+ if not compilation_options.commands:
69
+ # Language is not compiled.
70
+ return sandbox.file_cacher.put_file_from_path(generator_path)
71
+
72
+ # Compile the generator
73
+ commands = get_mapped_commands(compilation_options.commands, file_mapping)
74
+ if sys.platform == 'darwin':
75
+ commands = normalize_for_macos(commands)
76
+
77
+ compiled_digest = DigestHolder()
78
+
79
+ artifacts = GradingArtifacts()
80
+ artifacts.inputs.extend(
81
+ GradingFileInput(src=src, dest=dest)
82
+ for src, dest in package.get_compilation_files(code)
83
+ )
84
+ download.maybe_add_testlib(code, artifacts)
85
+ download.maybe_add_jngen(code, artifacts)
86
+ artifacts.inputs.append(
87
+ GradingFileInput(src=generator_path, dest=PosixPath(file_mapping.compilable))
88
+ )
89
+ artifacts.outputs.append(
90
+ GradingFileOutput(
91
+ src=PosixPath(file_mapping.executable),
92
+ digest=compiled_digest,
93
+ executable=True,
94
+ )
95
+ )
96
+
97
+ if not steps_with_caching.compile(
98
+ commands,
99
+ params=sandbox_params,
100
+ artifacts=artifacts,
101
+ sandbox=sandbox,
102
+ dependency_cache=dependency_cache,
103
+ ):
104
+ raise typer.Exit(1)
105
+
106
+ assert compiled_digest.value is not None
107
+ return compiled_digest.value
108
+
109
+
110
+ def run_item(
111
+ code: CodeItem,
112
+ executable: DigestOrSource,
113
+ stdin: Optional[DigestOrSource] = None,
114
+ stdout: Optional[DigestOrDest] = None,
115
+ stderr: Optional[DigestOrDest] = None,
116
+ inputs: Optional[List[GradingFileInput]] = None,
117
+ outputs: Optional[List[GradingFileOutput]] = None,
118
+ extra_args: Optional[str] = None,
119
+ extra_config: Optional[ExecutionConfig] = None,
120
+ ) -> Optional[RunLog]:
121
+ language = find_language_name(code)
122
+ execution_options = get_execution_config(language)
123
+ if extra_config is not None:
124
+ execution_options = merge_execution_configs([execution_options, extra_config])
125
+ file_mapping = get_file_mapping(language)
126
+ dependency_cache = package.get_dependency_cache()
127
+ sandbox = package.get_singleton_sandbox()
128
+ sandbox_params = get_sandbox_params_from_config(execution_options.sandbox)
129
+
130
+ sandbox_params.set_stdall(
131
+ stdin=PosixPath(file_mapping.input) if stdin is not None else None,
132
+ stdout=PosixPath(file_mapping.output) if stdout is not None else None,
133
+ stderr=PosixPath(file_mapping.error) if stderr is not None else None,
134
+ )
135
+
136
+ assert execution_options.command
137
+ command = get_mapped_command(execution_options.command, file_mapping)
138
+
139
+ if extra_args is not None:
140
+ splitted_command = shlex.split(command)
141
+ splitted_command.extend(shlex.split(extra_args))
142
+ command = shlex.join(splitted_command)
143
+
144
+ artifacts = GradingArtifacts()
145
+ artifacts.inputs.append(
146
+ GradingFileInput(
147
+ **executable.expand(),
148
+ dest=PosixPath(file_mapping.executable),
149
+ executable=True,
150
+ )
151
+ )
152
+ if stdin is not None:
153
+ artifacts.inputs.append(
154
+ GradingFileInput(
155
+ **stdin.expand(),
156
+ dest=PosixPath(file_mapping.input),
157
+ )
158
+ )
159
+ if stdout is not None:
160
+ artifacts.outputs.append(
161
+ GradingFileOutput(
162
+ src=PosixPath(file_mapping.output),
163
+ **stdout.expand(),
164
+ )
165
+ )
166
+ if stderr is not None:
167
+ artifacts.outputs.append(
168
+ GradingFileOutput(
169
+ src=PosixPath(file_mapping.error),
170
+ **stderr.expand(),
171
+ )
172
+ )
173
+ if inputs:
174
+ artifacts.inputs.extend(inputs)
175
+ if outputs:
176
+ artifacts.outputs.extend(outputs)
177
+
178
+ return steps_with_caching.run(
179
+ command,
180
+ params=sandbox_params,
181
+ sandbox=sandbox,
182
+ artifacts=artifacts,
183
+ dependency_cache=dependency_cache,
184
+ metadata=RunLogMetadata(language=code.language),
185
+ )
rbx/box/compile.py ADDED
@@ -0,0 +1,56 @@
1
+ import pathlib
2
+
3
+ import typer
4
+
5
+ from rbx import annotations, console
6
+ from rbx.box import code, package
7
+ from rbx.box.schema import CodeItem
8
+
9
+ app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
10
+
11
+
12
+ def _compile_out():
13
+ return package.get_build_path() / 'exe'
14
+
15
+
16
+ def _compile(item: CodeItem):
17
+ console.console.print(f'Compiling [item]{item.path}[/item]...')
18
+ digest = code.compile_item(item)
19
+ cacher = package.get_file_cacher()
20
+ out_path = _compile_out()
21
+ cacher.get_file_to_path(digest, out_path)
22
+ out_path.chmod(0o755)
23
+
24
+ console.console.print(
25
+ f'[success]Compiled file written at [item]{out_path}[/item].[/success]'
26
+ )
27
+
28
+
29
+ @app.command('any, a', help='Compile an asset given its path.')
30
+ @package.within_problem
31
+ def any(path: str):
32
+ _compile(CodeItem(path=pathlib.Path(path)))
33
+
34
+
35
+ @app.command('solution, s', help='Compile a solution given its path.')
36
+ @package.within_problem
37
+ def solution(path: str):
38
+ _compile(package.get_solution(path))
39
+
40
+
41
+ @app.command('generator, gen, g', help='Compile a generator given its name.')
42
+ @package.within_problem
43
+ def generator(name: str):
44
+ _compile(package.get_generator(name))
45
+
46
+
47
+ @app.command('checker, c', help='Compile the checker.')
48
+ @package.within_problem
49
+ def checker():
50
+ _compile(package.get_checker())
51
+
52
+
53
+ @app.command('validator, v', help='Compile the main validator.')
54
+ @package.within_problem
55
+ def validator():
56
+ _compile(package.get_validator())
rbx/box/conftest.py ADDED
@@ -0,0 +1,42 @@
1
+ import os
2
+ import pathlib
3
+ import shutil
4
+ from collections.abc import Iterator
5
+
6
+ import pytest
7
+
8
+ from rbx import testing_utils
9
+ from rbx.box import package
10
+
11
+
12
+ @pytest.fixture
13
+ def pkg_cleandir(cleandir: pathlib.Path) -> Iterator[pathlib.Path]:
14
+ old_temp_dir = package.TEMP_DIR
15
+ package.TEMP_DIR = cleandir
16
+
17
+ pkgdir = cleandir / 'pkg'
18
+ pkgdir.mkdir(exist_ok=True, parents=True)
19
+ lwd = pathlib.Path.cwd()
20
+ os.chdir(str(pkgdir))
21
+ try:
22
+ yield pkgdir.absolute()
23
+ finally:
24
+ os.chdir(str(lwd))
25
+ package.TEMP_DIR = old_temp_dir
26
+
27
+
28
+ @pytest.fixture
29
+ def pkg_from_testdata(
30
+ request, testdata_path: pathlib.Path, pkg_cleandir: pathlib.Path
31
+ ) -> Iterator[pathlib.Path]:
32
+ marker = request.node.get_closest_marker('test_pkg')
33
+ if marker is None:
34
+ raise ValueError('test_pkg marker not found')
35
+ testdata = testdata_path / marker.args[0]
36
+ shutil.copytree(str(testdata), str(pkg_cleandir), dirs_exist_ok=True)
37
+ yield pkg_cleandir
38
+
39
+
40
+ @pytest.fixture(autouse=True)
41
+ def clear_cache():
42
+ testing_utils.clear_all_functools_cache()
File without changes