cuju-render 0.1.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.
- cuju/render/__init__.py +759 -0
- cuju/render/__main__.py +1065 -0
- cuju/render/_export/__init__.py +3 -0
- cuju/render/_export/feed.py +542 -0
- cuju/render/_export/sizes.py +157 -0
- cuju/render/_export/video.py +1618 -0
- cuju/render/_export/video_jobs.py +408 -0
- cuju/render/_format/__init__.py +3 -0
- cuju/render/_format/annotations.py +235 -0
- cuju/render/_format/codec.py +307 -0
- cuju/render/_format/keep.py +121 -0
- cuju/render/_format/recording.py +860 -0
- cuju/render/_format/writer.py +54 -0
- cuju/render/_guide/__init__.py +3 -0
- cuju/render/_guide/doctor.py +523 -0
- cuju/render/_guide/help.py +62 -0
- cuju/render/_guide/tutorial.py +254 -0
- cuju/render/_http/__init__.py +3 -0
- cuju/render/_http/commands.py +57 -0
- cuju/render/_http/headers.py +103 -0
- cuju/render/_http/launch.py +500 -0
- cuju/render/_http/launchers.py +408 -0
- cuju/render/_http/pwa.py +128 -0
- cuju/render/_http/server.py +2105 -0
- cuju/render/_http/window.py +360 -0
- cuju/render/_moments/__init__.py +3 -0
- cuju/render/_moments/captions.py +284 -0
- cuju/render/_moments/events.py +1123 -0
- cuju/render/_moments/search.py +694 -0
- cuju/render/_packages/__init__.py +3 -0
- cuju/render/_packages/glsl.py +746 -0
- cuju/render/_packages/loudness.py +334 -0
- cuju/render/_packages/pack.py +1959 -0
- cuju/render/_packages/presentation.py +1376 -0
- cuju/render/_packages/vocabulary.py +609 -0
- cuju/render/_playing/__init__.py +3 -0
- cuju/render/_playing/achievements.py +463 -0
- cuju/render/_playing/bench.py +609 -0
- cuju/render/_playing/challenges.py +1813 -0
- cuju/render/_playing/demo.py +323 -0
- cuju/render/_playing/draw.py +211 -0
- cuju/render/_playing/errors.py +20 -0
- cuju/render/_playing/game.py +645 -0
- cuju/render/_playing/limits.py +339 -0
- cuju/render/_playing/play.py +926 -0
- cuju/render/_playing/policy_worker.py +250 -0
- cuju/render/_playing/strict.py +810 -0
- cuju/render/_registry/__init__.py +3 -0
- cuju/render/_registry/clips.py +394 -0
- cuju/render/_registry/joints.py +108 -0
- cuju/render/_registry/teams.py +2058 -0
- cuju/render/_store/__init__.py +3 -0
- cuju/render/_store/home.py +70 -0
- cuju/render/_store/library.py +604 -0
- cuju/render/_store/samples.py +163 -0
- cuju/render/_text/__init__.py +3 -0
- cuju/render/_text/languages.py +637 -0
- cuju/render/_text/messages.py +1396 -0
- cuju/render/_tournaments/__init__.py +3 -0
- cuju/render/_tournaments/results.py +547 -0
- cuju/render/_tournaments/tournament.py +2742 -0
- cuju/render/_tournaments/tournament_cli.py +587 -0
- cuju/render/_tournaments/tournament_runner.py +1975 -0
- cuju/render/_version.py +6 -0
- cuju/render/_watch/__init__.py +3 -0
- cuju/render/_watch/grid.py +265 -0
- cuju/render/_watch/live.py +664 -0
- cuju/render/_watch/notebook.py +79 -0
- cuju/render/_watch/show.py +171 -0
- cuju/render/samples/sample-11v11.cjr +0 -0
- cuju/render/samples/sample-5v5.cjr +0 -0
- cuju/render/samples/teams/starter/main.py +33 -0
- cuju/render/scenarios/benchmark_full_game_11_vs_11_easy.py +46 -0
- cuju/render/scenarios/benchmark_full_game_11_vs_11_hard.py +46 -0
- cuju/render/scenarios/benchmark_full_game_11_vs_11_medium.py +46 -0
- cuju/render/scenarios/benchmark_full_game_5_vs_5_easy.py +34 -0
- cuju/render/scenarios/benchmark_full_game_5_vs_5_hard.py +34 -0
- cuju/render/scenarios/benchmark_full_game_5_vs_5_medium.py +34 -0
- cuju/render/web/assets/adboard_polygon.webp +0 -0
- cuju/render/web/assets/adboard_your_ad_here.webp +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-combatmelee.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-general.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-movementadvanced.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-movementbasic.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-simulation.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-special.glb +0 -0
- cuju/render/web/assets/ball.glb +0 -0
- cuju/render/web/assets/challenges/3_vs_1_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/3_vs_1_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/corner.mp4 +0 -0
- cuju/render/web/assets/challenges/corner.webp +0 -0
- cuju/render/web/assets/challenges/counterattack_easy.mp4 +0 -0
- cuju/render/web/assets/challenges/counterattack_easy.webp +0 -0
- cuju/render/web/assets/challenges/counterattack_hard.mp4 +0 -0
- cuju/render/web/assets/challenges/counterattack_hard.webp +0 -0
- cuju/render/web/assets/challenges/empty_goal.mp4 +0 -0
- cuju/render/web/assets/challenges/empty_goal.webp +0 -0
- cuju/render/web/assets/challenges/empty_goal_close.mp4 +0 -0
- cuju/render/web/assets/challenges/empty_goal_close.webp +0 -0
- cuju/render/web/assets/challenges/manifest.json +375 -0
- cuju/render/web/assets/challenges/one_on_one.mp4 +0 -0
- cuju/render/web/assets/challenges/one_on_one.webp +0 -0
- cuju/render/web/assets/challenges/one_on_one_hard.mp4 +0 -0
- cuju/render/web/assets/challenges/one_on_one_hard.webp +0 -0
- cuju/render/web/assets/challenges/pass_and_shoot_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/pass_and_shoot_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/run_pass_and_shoot_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/run_pass_and_shoot_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/run_to_score.mp4 +0 -0
- cuju/render/web/assets/challenges/run_to_score.webp +0 -0
- cuju/render/web/assets/challenges/run_to_score_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/run_to_score_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/single_goal_versus_lazy.mp4 +0 -0
- cuju/render/web/assets/challenges/single_goal_versus_lazy.webp +0 -0
- cuju/render/web/assets/crowd.webp +0 -0
- cuju/render/web/assets/crowd_normal.webp +0 -0
- cuju/render/web/assets/cuju-logo-384.png +0 -0
- cuju/render/web/assets/cuju-logo.png +0 -0
- cuju/render/web/assets/cuju-logo.svg +46 -0
- cuju/render/web/assets/cuju-mark-light.svg +33 -0
- cuju/render/web/assets/cuju-mark.svg +33 -0
- cuju/render/web/assets/goal.glb +0 -0
- cuju/render/web/assets/grass.webp +0 -0
- cuju/render/web/assets/hair_black.png +0 -0
- cuju/render/web/assets/hair_blonde.png +0 -0
- cuju/render/web/assets/hair_brown.png +0 -0
- cuju/render/web/assets/hair_darkblonde.png +0 -0
- cuju/render/web/assets/hair_long01.glb +0 -0
- cuju/render/web/assets/hair_long02.glb +0 -0
- cuju/render/web/assets/hair_medium01.glb +0 -0
- cuju/render/web/assets/hair_red.png +0 -0
- cuju/render/web/assets/hair_short01.glb +0 -0
- cuju/render/web/assets/hair_short02.glb +0 -0
- cuju/render/web/assets/icons/cuju-192.png +0 -0
- cuju/render/web/assets/icons/cuju-512.png +0 -0
- cuju/render/web/assets/icons/cuju-maskable-512.png +0 -0
- cuju/render/web/assets/kit_goalkeeper.webp +0 -0
- cuju/render/web/assets/kit_mask.png +0 -0
- cuju/render/web/assets/kit_normal.webp +0 -0
- cuju/render/web/assets/manifest.json +601 -0
- cuju/render/web/assets/models/kaykit-barbarian.glb +0 -0
- cuju/render/web/assets/models/kaykit-barbarian.json +23 -0
- cuju/render/web/assets/models/kaykit-knight.glb +0 -0
- cuju/render/web/assets/models/kaykit-knight.json +23 -0
- cuju/render/web/assets/models/kaykit-mage.glb +0 -0
- cuju/render/web/assets/models/kaykit-mage.json +23 -0
- cuju/render/web/assets/models/kaykit-mannequin-medium.glb +0 -0
- cuju/render/web/assets/models/kaykit-mannequin-medium.json +23 -0
- cuju/render/web/assets/models/kaykit-ranger.glb +0 -0
- cuju/render/web/assets/models/kaykit-ranger.json +23 -0
- cuju/render/web/assets/models/kaykit-rogue-hooded.glb +0 -0
- cuju/render/web/assets/models/kaykit-rogue-hooded.json +23 -0
- cuju/render/web/assets/models/kaykit-rogue.glb +0 -0
- cuju/render/web/assets/models/kaykit-rogue.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-mage.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-mage.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-minion.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-minion.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-rogue.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-rogue.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-warrior.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-warrior.json +23 -0
- cuju/render/web/assets/pitch.webp +0 -0
- cuju/render/web/assets/pitch_normal.webp +0 -0
- cuju/render/web/assets/player.glb +0 -0
- cuju/render/web/assets/shoe.webp +0 -0
- cuju/render/web/assets/shoe_sole.png +0 -0
- cuju/render/web/assets/skin_01.png +0 -0
- cuju/render/web/assets/skin_02.png +0 -0
- cuju/render/web/assets/skin_03.png +0 -0
- cuju/render/web/assets/skin_04.png +0 -0
- cuju/render/web/assets/sounds/applause.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-1.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-2.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-3.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-4.ogg +0 -0
- cuju/render/web/assets/sounds/city-horn.ogg +0 -0
- cuju/render/web/assets/sounds/city-loop.ogg +0 -0
- cuju/render/web/assets/sounds/city-siren.ogg +0 -0
- cuju/render/web/assets/sounds/crowd-cheer.ogg +0 -0
- cuju/render/web/assets/sounds/crowd-loop.ogg +0 -0
- cuju/render/web/assets/sounds/crowd-ooh.ogg +0 -0
- cuju/render/web/assets/sounds/gulls.ogg +0 -0
- cuju/render/web/assets/sounds/neon-sting.ogg +0 -0
- cuju/render/web/assets/sounds/rain-loop.ogg +0 -0
- cuju/render/web/assets/sounds/sounds.json +46 -0
- cuju/render/web/assets/sounds/space-beep.ogg +0 -0
- cuju/render/web/assets/sounds/space-hum.ogg +0 -0
- cuju/render/web/assets/sounds/space-radio.ogg +0 -0
- cuju/render/web/assets/sounds/whistle-long.ogg +0 -0
- cuju/render/web/assets/sounds/whistle-short.ogg +0 -0
- cuju/render/web/assets/sounds/wind-loop.ogg +0 -0
- cuju/render/web/easy.css +357 -0
- cuju/render/web/export.html +23 -0
- cuju/render/web/feed.css +262 -0
- cuju/render/web/feed.html +28 -0
- cuju/render/web/flat.css +120 -0
- cuju/render/web/home.css +837 -0
- cuju/render/web/index.html +373 -0
- cuju/render/web/js/boot.js +50 -0
- cuju/render/web/js/broadcast/audio.js +2141 -0
- cuju/render/web/js/broadcast/celebrate.js +1238 -0
- cuju/render/web/js/broadcast/decisions.js +609 -0
- cuju/render/web/js/broadcast/hud.js +352 -0
- cuju/render/web/js/broadcast/show.js +3614 -0
- cuju/render/web/js/broadcast/transition.js +2231 -0
- cuju/render/web/js/broadcast/voices.js +153 -0
- cuju/render/web/js/capture.js +101 -0
- cuju/render/web/js/core/accessibility.js +90 -0
- cuju/render/web/js/core/api.js +429 -0
- cuju/render/web/js/core/comfort.js +135 -0
- cuju/render/web/js/core/i18n.js +705 -0
- cuju/render/web/js/core/icons.js +167 -0
- cuju/render/web/js/core/input-settings.js +156 -0
- cuju/render/web/js/core/keys.js +277 -0
- cuju/render/web/js/core/language-ui.js +125 -0
- cuju/render/web/js/core/menu.js +853 -0
- cuju/render/web/js/core/names.js +113 -0
- cuju/render/web/js/core/page-structure.js +110 -0
- cuju/render/web/js/core/scenarios.js +109 -0
- cuju/render/web/js/core/spoilers.js +236 -0
- cuju/render/web/js/core/typeahead.js +38 -0
- cuju/render/web/js/export/dom-picture.js +330 -0
- cuju/render/web/js/export/export-host.js +97 -0
- cuju/render/web/js/export/feed-dialog.js +191 -0
- cuju/render/web/js/export/feed-params.js +266 -0
- cuju/render/web/js/export/feed.js +363 -0
- cuju/render/web/js/export/mp4-mux.js +295 -0
- cuju/render/web/js/export/photo-core.js +313 -0
- cuju/render/web/js/export/photo.js +1314 -0
- cuju/render/web/js/export/safe-frame.js +59 -0
- cuju/render/web/js/export/size-fields.js +103 -0
- cuju/render/web/js/export/sizes.js +178 -0
- cuju/render/web/js/export/video-boot.js +55 -0
- cuju/render/web/js/export/video-browser.js +325 -0
- cuju/render/web/js/export/video-export.js +417 -0
- cuju/render/web/js/export/video-plan.js +207 -0
- cuju/render/web/js/export/video-range.js +185 -0
- cuju/render/web/js/export/webm-mux.js +198 -0
- cuju/render/web/js/feed-boot.js +151 -0
- cuju/render/web/js/feed-frame.js +101 -0
- cuju/render/web/js/help/doctor.js +488 -0
- cuju/render/web/js/help/firstrun.js +379 -0
- cuju/render/web/js/help/help-center.js +438 -0
- cuju/render/web/js/help/install-offer.js +164 -0
- cuju/render/web/js/help/install-state.js +104 -0
- cuju/render/web/js/help/lessons-core.js +207 -0
- cuju/render/web/js/help/lessons.js +463 -0
- cuju/render/web/js/help/palette.js +584 -0
- cuju/render/web/js/help/pwa.js +257 -0
- cuju/render/web/js/help/tour-core.js +241 -0
- cuju/render/web/js/help/tour.js +461 -0
- cuju/render/web/js/main.js +2513 -0
- cuju/render/web/js/pages/draw.js +188 -0
- cuju/render/web/js/pages/fieldpage.js +530 -0
- cuju/render/web/js/pages/home.js +521 -0
- cuju/render/web/js/pages/matches.js +779 -0
- cuju/render/web/js/pages/moments-ui.js +359 -0
- cuju/render/web/js/pages/queue-filters.js +63 -0
- cuju/render/web/js/pages/recording-groups.js +80 -0
- cuju/render/web/js/pages/recordings.js +1069 -0
- cuju/render/web/js/play/bench.js +347 -0
- cuju/render/web/js/play/challenges.js +1302 -0
- cuju/render/web/js/play/medals.js +127 -0
- cuju/render/web/js/play/play.js +1953 -0
- cuju/render/web/js/play/teasers.js +196 -0
- cuju/render/web/js/play/touch.js +350 -0
- cuju/render/web/js/play/tournament-layout.js +487 -0
- cuju/render/web/js/play/tournament-results.js +441 -0
- cuju/render/web/js/play/tournament-strict.js +560 -0
- cuju/render/web/js/play/tournament.js +2585 -0
- cuju/render/web/js/players/animate.js +972 -0
- cuju/render/web/js/players/clips.js +224 -0
- cuju/render/web/js/players/kaykit.js +162 -0
- cuju/render/web/js/players/kits.js +676 -0
- cuju/render/web/js/players/number-patch.js +82 -0
- cuju/render/web/js/players/players.js +2694 -0
- cuju/render/web/js/players/recolor.js +893 -0
- cuju/render/web/js/players/retarget.js +325 -0
- cuju/render/web/js/players/rig-numbers.js +174 -0
- cuju/render/web/js/presentation/animator.js +231 -0
- cuju/render/web/js/presentation/assign.js +85 -0
- cuju/render/web/js/presentation/behaviors.js +440 -0
- cuju/render/web/js/presentation/budget.js +209 -0
- cuju/render/web/js/presentation/catalog.js +1674 -0
- cuju/render/web/js/presentation/clocks.js +72 -0
- cuju/render/web/js/presentation/commands.js +301 -0
- cuju/render/web/js/presentation/contract.js +240 -0
- cuju/render/web/js/presentation/effects.js +904 -0
- cuju/render/web/js/presentation/flash.js +605 -0
- cuju/render/web/js/presentation/freeze-worklet.js +22 -0
- cuju/render/web/js/presentation/glsl.js +464 -0
- cuju/render/web/js/presentation/host.js +323 -0
- cuju/render/web/js/presentation/index.js +2066 -0
- cuju/render/web/js/presentation/loudness-worklet.js +235 -0
- cuju/render/web/js/presentation/loudness.js +740 -0
- cuju/render/web/js/presentation/moments.js +536 -0
- cuju/render/web/js/presentation/post.js +327 -0
- cuju/render/web/js/presentation/preview.js +180 -0
- cuju/render/web/js/presentation/protocol.js +174 -0
- cuju/render/web/js/presentation/reach.js +158 -0
- cuju/render/web/js/presentation/rigging.js +130 -0
- cuju/render/web/js/presentation/sandbox.js +157 -0
- cuju/render/web/js/presentation/shaders.js +235 -0
- cuju/render/web/js/presentation/stage.js +402 -0
- cuju/render/web/js/presentation/teamaudio.js +722 -0
- cuju/render/web/js/presentation/tracks.js +197 -0
- cuju/render/web/js/presentation/values.js +81 -0
- cuju/render/web/js/recording/annotation-data.js +203 -0
- cuju/render/web/js/recording/captions.js +491 -0
- cuju/render/web/js/recording/codec.js +185 -0
- cuju/render/web/js/recording/decode-worker.js +70 -0
- cuju/render/web/js/recording/decode.js +94 -0
- cuju/render/web/js/recording/episodes.js +46 -0
- cuju/render/web/js/recording/format.js +461 -0
- cuju/render/web/js/recording/live.js +348 -0
- cuju/render/web/js/recording/moments-query.js +59 -0
- cuju/render/web/js/recording/moments.js +149 -0
- cuju/render/web/js/recording/pose.js +86 -0
- cuju/render/web/js/recording/timeline.js +275 -0
- cuju/render/web/js/teams/art.js +50 -0
- cuju/render/web/js/teams/clip-editor.js +293 -0
- cuju/render/web/js/teams/editor.js +1979 -0
- cuju/render/web/js/teams/library.js +344 -0
- cuju/render/web/js/teams/painter.js +633 -0
- cuju/render/web/js/teams/surprise-ui.js +195 -0
- cuju/render/web/js/teams/surprise.js +497 -0
- cuju/render/web/js/teams/team-check.js +127 -0
- cuju/render/web/js/watch/annotations.js +270 -0
- cuju/render/web/js/watch/flat.js +366 -0
- cuju/render/web/js/watch/grid.js +436 -0
- cuju/render/web/js/watch/pitch2d.js +289 -0
- cuju/render/web/js/watch/player-tools.js +192 -0
- cuju/render/web/js/watch/presets.js +201 -0
- cuju/render/web/js/world/arenas.js +997 -0
- cuju/render/web/js/world/ball.js +204 -0
- cuju/render/web/js/world/cameras.js +982 -0
- cuju/render/web/js/world/crowd.js +376 -0
- cuju/render/web/js/world/field.js +227 -0
- cuju/render/web/js/world/framing.js +145 -0
- cuju/render/web/js/world/occlusion.js +48 -0
- cuju/render/web/js/world/pitch.js +703 -0
- cuju/render/web/js/world/scene.js +395 -0
- cuju/render/web/js/world/sky.js +325 -0
- cuju/render/web/js/world/stadium.js +1262 -0
- cuju/render/web/js/world/vendor-sky.js +367 -0
- cuju/render/web/js/world/weather.js +264 -0
- cuju/render/web/languages/en.json +4750 -0
- cuju/render/web/lessons.json +137 -0
- cuju/render/web/manifest.webmanifest +23 -0
- cuju/render/web/style.css +8399 -0
- cuju/render/web/sw.js +235 -0
- cuju/render/web/vendor/fabric/LICENSE +16 -0
- cuju/render/web/vendor/fabric/fabric.min.mjs +1 -0
- cuju/render/web/vendor/lucide/LICENSE +43 -0
- cuju/render/web/vendor/lucide/icons.js +141 -0
- cuju/render/web/vendor/three/LICENSE +21 -0
- cuju/render/web/vendor/three/build/three.core.js +60586 -0
- cuju/render/web/vendor/three/build/three.module.js +19719 -0
- cuju/render/web/vendor/three/examples/jsm/loaders/GLTFLoader.js +4925 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/EffectComposer.js +365 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/MaskPass.js +195 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/OutputPass.js +148 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/Pass.js +191 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/RenderPass.js +193 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/ShaderPass.js +135 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/UnrealBloomPass.js +541 -0
- cuju/render/web/vendor/three/examples/jsm/shaders/CopyShader.js +52 -0
- cuju/render/web/vendor/three/examples/jsm/shaders/LuminosityHighPassShader.js +68 -0
- cuju/render/web/vendor/three/examples/jsm/shaders/OutputShader.js +103 -0
- cuju/render/web/vendor/three/examples/jsm/utils/BufferGeometryUtils.js +1501 -0
- cuju/render/web/vendor/three/examples/jsm/utils/SkeletonUtils.js +496 -0
- cuju_render-0.1.0.dist-info/METADATA +116 -0
- cuju_render-0.1.0.dist-info/RECORD +382 -0
- cuju_render-0.1.0.dist-info/WHEEL +4 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSE +202 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/CC-BY-4.0.txt +396 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/CC0-1.0.txt +127 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/ISC.txt +16 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/MIT.txt +24 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/Unlicense.txt +13 -0
- cuju_render-0.1.0.dist-info/licenses/NOTICE +35 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Copyright 2026 The cuju Authors
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Runs one team's policy in a process of its own, for a match under strict rules.
|
|
5
|
+
|
|
6
|
+
The match starts this file as a script, with Python's option `-I`, so the process imports the
|
|
7
|
+
team's folder first and the installed packages after it, and nothing of the renderer, of another
|
|
8
|
+
team or of the folder it started in. Two teams so never share a module, even modules of the same
|
|
9
|
+
name. The variables of the usual libraries limit the process to the threads that the rules give,
|
|
10
|
+
and torch, once the team imports it, takes the same limit. This is isolation for fairness, so that
|
|
11
|
+
every team plays on the same terms, not a sandbox for code that one cannot trust.
|
|
12
|
+
|
|
13
|
+
The process loads the team's `main.py` as Kaggle does and takes its last callable as the agent. A
|
|
14
|
+
callable named `reset` that is not the agent is the team's reset, which the match calls before its
|
|
15
|
+
first observation. The process then answers one request at a time, each a line of JSON on a copy
|
|
16
|
+
of its standard input, with one line of JSON on a copy of its standard output. Before the team's
|
|
17
|
+
code runs, its standard input reads as empty and its standard output goes to standard error, which
|
|
18
|
+
the match writes to the team's log, so no team reads the requests or forges an answer. The
|
|
19
|
+
working folder is a private temporary folder that the match removes afterwards, so a team reads
|
|
20
|
+
its own files by their path, beside its `main.py`. The protocol is JSON alone in both directions,
|
|
21
|
+
and each side reads its pipe on a thread of its own, which works alike on every system.
|
|
22
|
+
|
|
23
|
+
- At its start it waits for `{"start": true}`, which the match sends once the process stands
|
|
24
|
+
under its limits, and then answers `{"ready": true}` once the agent loads, or the error.
|
|
25
|
+
- `{"reset": true}` calls the team's reset and answers `{"reset": true}`.
|
|
26
|
+
- `{"obs": ...}` calls the agent with the observation and answers `{"answer": ..., "compute": s}`,
|
|
27
|
+
the answer as JSON and the seconds that the call took.
|
|
28
|
+
- An error answers `{"error": text, "stage": stage}`, the stage being `load`, `reset`, `call` or
|
|
29
|
+
`answer`, the last for an answer that is no JSON, or `memory` for memory that ran out and
|
|
30
|
+
`process` for a process that the team tried to start, which the rules refuse.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import contextlib
|
|
34
|
+
import inspect
|
|
35
|
+
import json
|
|
36
|
+
import os
|
|
37
|
+
import sys
|
|
38
|
+
import threading
|
|
39
|
+
import time
|
|
40
|
+
import traceback
|
|
41
|
+
import types
|
|
42
|
+
|
|
43
|
+
# The variables that set the threads of the usual libraries of numbers.
|
|
44
|
+
THREAD_VARIABLES = (
|
|
45
|
+
'OMP_NUM_THREADS',
|
|
46
|
+
'MKL_NUM_THREADS',
|
|
47
|
+
'OPENBLAS_NUM_THREADS',
|
|
48
|
+
'NUMEXPR_NUM_THREADS',
|
|
49
|
+
'VECLIB_MAXIMUM_THREADS',
|
|
50
|
+
'BLIS_NUM_THREADS',
|
|
51
|
+
)
|
|
52
|
+
_MAIN = 'main.py'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _Struct(dict):
|
|
56
|
+
"""A dictionary whose keys are also attributes, as Kaggle's observations are."""
|
|
57
|
+
|
|
58
|
+
def __getattr__(self, name):
|
|
59
|
+
try:
|
|
60
|
+
return self[name]
|
|
61
|
+
except KeyError:
|
|
62
|
+
raise AttributeError(name) from None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _takes_configuration(function):
|
|
66
|
+
"""Returns whether Kaggle would pass the configuration to an agent, as a second argument."""
|
|
67
|
+
try:
|
|
68
|
+
parameters = inspect.signature(function).parameters.values()
|
|
69
|
+
except (TypeError, ValueError):
|
|
70
|
+
return False
|
|
71
|
+
positional = [
|
|
72
|
+
parameter
|
|
73
|
+
for parameter in parameters
|
|
74
|
+
if parameter.kind in (parameter.POSITIONAL_ONLY, parameter.POSITIONAL_OR_KEYWORD)
|
|
75
|
+
]
|
|
76
|
+
return len(positional) >= 2 or any(
|
|
77
|
+
parameter.kind == parameter.VAR_POSITIONAL for parameter in parameters
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _plain(value):
|
|
82
|
+
"""Returns an answer as JSON values, an array or an enum as Kaggle's helpers make them."""
|
|
83
|
+
if hasattr(value, 'tolist') and callable(value.tolist):
|
|
84
|
+
value = value.tolist()
|
|
85
|
+
if isinstance(value, (list, tuple)):
|
|
86
|
+
return [_plain(item) for item in value]
|
|
87
|
+
if not isinstance(value, (int, float, str, bool)) and value is not None:
|
|
88
|
+
found = getattr(value, 'value', None)
|
|
89
|
+
if isinstance(found, int):
|
|
90
|
+
return found
|
|
91
|
+
return value
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _limit_torch(threads):
|
|
95
|
+
"""Gives torch the limit of threads, once the team has imported it."""
|
|
96
|
+
torch = sys.modules.get('torch')
|
|
97
|
+
if torch is None:
|
|
98
|
+
return
|
|
99
|
+
with contextlib.suppress(Exception):
|
|
100
|
+
torch.set_num_threads(threads)
|
|
101
|
+
with contextlib.suppress(Exception):
|
|
102
|
+
torch.set_num_interop_threads(threads)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _reason(error):
|
|
106
|
+
return f'{type(error).__name__}: {error}'
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# The audit events of Python that start another process, which strict rules refuse a team.
|
|
110
|
+
_PROCESS_EVENTS = frozenset(
|
|
111
|
+
{
|
|
112
|
+
'subprocess.Popen',
|
|
113
|
+
'os.system',
|
|
114
|
+
'os.posix_spawn',
|
|
115
|
+
'os.fork',
|
|
116
|
+
'os.forkpty',
|
|
117
|
+
'os.exec',
|
|
118
|
+
'os.spawn',
|
|
119
|
+
'os.startfile',
|
|
120
|
+
'pty.spawn',
|
|
121
|
+
'_winapi.CreateProcess',
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _refuse_processes(answer):
|
|
127
|
+
"""Ends the process with its reason when the team starts another, by an audit hook."""
|
|
128
|
+
|
|
129
|
+
def hook(event, arguments):
|
|
130
|
+
if event in _PROCESS_EVENTS:
|
|
131
|
+
with contextlib.suppress(Exception):
|
|
132
|
+
answer(error=f'the team started another process, {event}', stage='process')
|
|
133
|
+
os._exit(1)
|
|
134
|
+
|
|
135
|
+
sys.addaudithook(hook)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _stage(error, stage):
|
|
139
|
+
"""Returns the stage of an error, `memory` for memory that ran out whatever the stage."""
|
|
140
|
+
return 'memory' if isinstance(error, MemoryError) else stage
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _watch(parent):
|
|
144
|
+
"""Ends this process once the match that started it ends, even while the team computes."""
|
|
145
|
+
if os.name == 'nt':
|
|
146
|
+
import ctypes
|
|
147
|
+
|
|
148
|
+
kernel = ctypes.windll.kernel32
|
|
149
|
+
# SYNCHRONIZE, which lets the wait below see the parent end.
|
|
150
|
+
handle = kernel.OpenProcess(0x00100000, False, parent)
|
|
151
|
+
if not handle:
|
|
152
|
+
os._exit(1)
|
|
153
|
+
kernel.WaitForSingleObject(handle, 0xFFFFFFFF)
|
|
154
|
+
os._exit(1)
|
|
155
|
+
while os.getppid() == parent:
|
|
156
|
+
time.sleep(0.5)
|
|
157
|
+
os._exit(1)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def main():
|
|
161
|
+
folder = os.path.abspath(sys.argv[1])
|
|
162
|
+
threads = int(sys.argv[2])
|
|
163
|
+
threading.Thread(target=_watch, args=(os.getppid(),), daemon=True).start()
|
|
164
|
+
# The requests and the answers keep copies of standard input and output, and the team's own
|
|
165
|
+
# standard input then reads as empty while its prints go to standard error.
|
|
166
|
+
sys.stdout.flush()
|
|
167
|
+
channel = os.fdopen(os.dup(1), 'w', encoding='utf-8')
|
|
168
|
+
requests = os.fdopen(os.dup(0), 'r', encoding='utf-8')
|
|
169
|
+
os.dup2(2, 1)
|
|
170
|
+
sys.stdout = sys.stderr
|
|
171
|
+
empty = os.open(os.devnull, os.O_RDONLY)
|
|
172
|
+
os.dup2(empty, 0)
|
|
173
|
+
os.close(empty)
|
|
174
|
+
sys.stdin = open(os.devnull, encoding='utf-8')
|
|
175
|
+
|
|
176
|
+
def answer(**entries):
|
|
177
|
+
channel.write(json.dumps(entries) + '\n')
|
|
178
|
+
channel.flush()
|
|
179
|
+
|
|
180
|
+
# The match holds the process to its limits before any of the team's code runs.
|
|
181
|
+
for line in requests:
|
|
182
|
+
with contextlib.suppress(ValueError):
|
|
183
|
+
if json.loads(line).get('start'):
|
|
184
|
+
break
|
|
185
|
+
else:
|
|
186
|
+
return
|
|
187
|
+
sys.path.insert(0, folder)
|
|
188
|
+
module = types.ModuleType('main')
|
|
189
|
+
module.__file__ = os.path.join(folder, _MAIN)
|
|
190
|
+
try:
|
|
191
|
+
with open(module.__file__, 'rb') as stream:
|
|
192
|
+
source = stream.read()
|
|
193
|
+
# Kaggle: `get_last_callable` runs the file and takes the last callable it defined.
|
|
194
|
+
exec(compile(source, module.__file__, 'exec'), module.__dict__)
|
|
195
|
+
callables = [value for value in module.__dict__.values() if callable(value)]
|
|
196
|
+
if not callables:
|
|
197
|
+
raise LookupError('main.py defines no function')
|
|
198
|
+
agent = callables[-1]
|
|
199
|
+
reset = module.__dict__.get('reset')
|
|
200
|
+
if not callable(reset) or reset is agent:
|
|
201
|
+
reset = None
|
|
202
|
+
two = _takes_configuration(agent)
|
|
203
|
+
except BaseException as error:
|
|
204
|
+
traceback.print_exc()
|
|
205
|
+
answer(error=_reason(error), stage=_stage(error, 'load'))
|
|
206
|
+
return
|
|
207
|
+
_limit_torch(threads)
|
|
208
|
+
# Libraries may run a short helper process while they load, as Python's ctypes does to find a
|
|
209
|
+
# library on Linux, so the refusal starts once the team has loaded.
|
|
210
|
+
_refuse_processes(answer)
|
|
211
|
+
configuration = _Struct()
|
|
212
|
+
answer(ready=True)
|
|
213
|
+
for line in requests:
|
|
214
|
+
try:
|
|
215
|
+
request = json.loads(line)
|
|
216
|
+
except ValueError:
|
|
217
|
+
continue
|
|
218
|
+
if not isinstance(request, dict) or request.get('end'):
|
|
219
|
+
break
|
|
220
|
+
if request.get('reset'):
|
|
221
|
+
try:
|
|
222
|
+
if reset is not None:
|
|
223
|
+
reset()
|
|
224
|
+
except BaseException as error:
|
|
225
|
+
traceback.print_exc()
|
|
226
|
+
answer(error=_reason(error), stage=_stage(error, 'reset'))
|
|
227
|
+
return
|
|
228
|
+
_limit_torch(threads)
|
|
229
|
+
answer(reset=True)
|
|
230
|
+
continue
|
|
231
|
+
obs = _Struct(request.get('obs') or {})
|
|
232
|
+
started = time.perf_counter()
|
|
233
|
+
try:
|
|
234
|
+
value = agent(obs, configuration) if two else agent(obs)
|
|
235
|
+
except BaseException as error:
|
|
236
|
+
traceback.print_exc()
|
|
237
|
+
answer(error=_reason(error), stage=_stage(error, 'call'))
|
|
238
|
+
return
|
|
239
|
+
compute = time.perf_counter() - started
|
|
240
|
+
try:
|
|
241
|
+
text = json.dumps({'answer': _plain(value), 'compute': compute})
|
|
242
|
+
except (TypeError, ValueError) as error:
|
|
243
|
+
answer(error=f'the answer {value!r:.200} is no action, {error}', stage='answer')
|
|
244
|
+
return
|
|
245
|
+
channel.write(text + '\n')
|
|
246
|
+
channel.flush()
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
if __name__ == '__main__':
|
|
250
|
+
main()
|